moved from sandbox
This commit is contained in:
parent
127caef1a6
commit
f206cd649d
|
@ -0,0 +1,240 @@
|
|||
#ifndef GCACHE_CACHE_H
|
||||
#define GCACHE_CACHE_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <vector>
|
||||
|
||||
#include <QThread>
|
||||
#include "provider.h"
|
||||
|
||||
/* this cache system enforce the rule that the items in a cache are always in all the cache below */
|
||||
/* two mechanism to remove tokens from the cache:
|
||||
1) set token count to something low
|
||||
2) set maximum number of tokens in the provider
|
||||
*/
|
||||
|
||||
/** Cache virtual base class. You are required to implement the pure virtual functions get, drop and size.
|
||||
*/
|
||||
|
||||
template <typename Token>
|
||||
class Cache: public Provider<Token> {
|
||||
|
||||
public:
|
||||
bool final; //true if this is the last cache (the one we use the data from)
|
||||
bool quit; //graceful exit
|
||||
bool waiting;
|
||||
///data is fetched from here
|
||||
Provider<Token> *input;
|
||||
|
||||
protected:
|
||||
///max space available
|
||||
quint64 s_max;
|
||||
///current space used
|
||||
quint64 s_curr;
|
||||
|
||||
public:
|
||||
Cache(quint64 _capacity = INT_MAX):
|
||||
final(false), quit(false), waiting(false), input(NULL), s_max(_capacity), s_curr(0) {}
|
||||
virtual ~Cache() {}
|
||||
|
||||
void setInputCache(Provider<Token> *p) { input = p; }
|
||||
quint64 capacity() { return s_max; }
|
||||
quint64 size() { return s_curr; }
|
||||
void setCapacity(quint64 c) { s_max = c; }
|
||||
///return true if the cache is waiting for priority to change
|
||||
bool isWaiting() { return waiting; }
|
||||
|
||||
///empty the cache. Make sure no resource is locked before calling this.
|
||||
void flush() {
|
||||
std::vector<Token *> tokens;
|
||||
{
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
for(int i = 0; i < this->heap.size(); i++) {
|
||||
Token *token = &(this->heap[i]);
|
||||
tokens.push_back(token);
|
||||
s_curr -= drop(token);
|
||||
assert(!(token->count >= Token::LOCKED));
|
||||
if(final)
|
||||
token->count.testAndSetOrdered(Token::READY, Token::CACHE);
|
||||
}
|
||||
this->heap.clear();
|
||||
}
|
||||
|
||||
assert(s_curr == 0);
|
||||
|
||||
{
|
||||
QMutexLocker locker(&(input->heap_lock));
|
||||
for(unsigned int i = 0; i < tokens.size(); i++) {
|
||||
input->heap.push(tokens[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///ensure there no locked item
|
||||
template <class FUNCTOR> void flush(FUNCTOR functor) {
|
||||
std::vector<Token *> tokens;
|
||||
{
|
||||
int count = 0;
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
for(int k = 0; k < this->heap.size(); k++) {
|
||||
Token *token = &this->heap[k];
|
||||
if(functor(token)) { //drop it
|
||||
tokens.push_back(token);
|
||||
s_curr -= drop(token);
|
||||
assert(!token->count >= Token::LOCKED);
|
||||
if(final)
|
||||
token->count.testAndSetOrdered(Token::READY, Token::CACHE);
|
||||
} else
|
||||
this->heap.at(count++) = token;
|
||||
}
|
||||
this->heap.resize(count);
|
||||
this->heap_dirty = true;
|
||||
}
|
||||
{
|
||||
QMutexLocker locker(&(input->heap_lock));
|
||||
for(unsigned int i = 0; i < tokens.size(); i++) {
|
||||
input->heap.push(tokens[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
///return the space used in the cache by the loaded resource
|
||||
virtual int size(Token *token) = 0;
|
||||
///returns amount of space used in cache -1 for failed transfer
|
||||
virtual int get(Token *token) = 0;
|
||||
///return amount removed
|
||||
virtual int drop(Token *token) = 0;
|
||||
|
||||
///called in as first thing in run()
|
||||
virtual void begin() {}
|
||||
///called in as last thing in run()
|
||||
virtual void end() {}
|
||||
|
||||
///[should be protected]
|
||||
void run() {
|
||||
assert(input);
|
||||
/* basic operation of the cache:
|
||||
1) transfer first element of input_cache if
|
||||
cache has room OR first element in input as higher priority of last element
|
||||
2) make room until eliminating an element would leave space. */
|
||||
begin();
|
||||
while(!this->quit) {
|
||||
waiting = true;
|
||||
input->check_queue.enter(true); //wait for cache below to load someghing or priorities to change
|
||||
waiting = false;
|
||||
|
||||
if(this->quit) break;
|
||||
|
||||
if(unload() || load())
|
||||
input->check_queue.open(); //we signal ourselves to check again
|
||||
}
|
||||
flush();
|
||||
this->quit = false; //in case someone wants to restart;
|
||||
end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///should be protected
|
||||
bool unload() {
|
||||
Token *remove = NULL;
|
||||
//make room int the cache checking that:
|
||||
//1 we need to make room (capacity < current)
|
||||
if(size() > capacity()) {
|
||||
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
|
||||
//2 we have some element not in the upper caches (heap.size() > 0
|
||||
if(this->heap.size()) {
|
||||
Token &last = this->heap.min();
|
||||
int itemsize = size(&last);
|
||||
|
||||
//3 after removing the item, we are still full (avoids bouncing items)
|
||||
if(size() - itemsize > capacity()) {
|
||||
|
||||
//4 item to remove is not locked. (only in last cache. you can't lock object otherwise)
|
||||
if(!final) { //not final we can drop when we want
|
||||
remove = this->heap.popMin();
|
||||
} else {
|
||||
last.count.testAndSetOrdered(Token::READY, Token::CACHE);
|
||||
if(last.count <= Token::CACHE) { //was not locked and now can't be locked, remove it.
|
||||
remove = this->heap.popMin();
|
||||
} else { //last item is locked need to reorder stack
|
||||
remove = this->heap.popMin();
|
||||
this->heap.push(remove);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(remove) {
|
||||
int size = drop(remove);
|
||||
assert(size >= 0);
|
||||
s_curr -= size;
|
||||
|
||||
{
|
||||
QMutexLocker input_locker(&(input->heap_lock));
|
||||
input->heap.push(remove);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
///should be protected
|
||||
bool load() {
|
||||
Token *insert = NULL;
|
||||
Token *last = NULL; //we want to lock only one heap at once to avoid deadlocks.
|
||||
|
||||
/* check wether we have room (curr < capacity) or heap is empty.
|
||||
empty heap is bad: we cannot drop anything to make room, and cache above has nothing to get.
|
||||
this should not happen if we set correct cache sizes, but if it happens.... */
|
||||
{
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
this->rebuild();
|
||||
if(size() > capacity() && this->heap.size() > 0) {
|
||||
last = &(this->heap.min()); //no room, set last so we might check for a swap.
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker input_locker(&(input->heap_lock));
|
||||
input->rebuild(); //if dirty rebuild
|
||||
if(input->heap.size()) { //we need something in input to tranfer.
|
||||
Token &first = input->heap.max();
|
||||
if(first.count > Token::REMOVE &&
|
||||
(!last || last->priority < first.priority)) { //if !last we already decided we want a transfer., otherwise check for a swap
|
||||
insert = input->heap.popMax(); //remove item from heap, while we transfer it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(insert) { //we want to fetch something
|
||||
|
||||
int size = get(insert);
|
||||
|
||||
if(size >= 0) { //success
|
||||
s_curr += size;
|
||||
{
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
if(final)
|
||||
insert->count.ref(); //now lock is 0 and can be locked
|
||||
|
||||
this->heap.push(insert);
|
||||
}
|
||||
this->check_queue.open(); //we should signal the parent cache that we have a new item
|
||||
return true;
|
||||
|
||||
} else { //failed transfer put it back, we will keep trying to transfer it...
|
||||
QMutexLocker input_locker(&(input->heap_lock));
|
||||
input->heap.push(insert);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GCACHE_H
|
|
@ -0,0 +1,131 @@
|
|||
#ifndef GCACHE_CONTROLLER_H
|
||||
#define GCACHE_CONTROLLER_H
|
||||
|
||||
#include "cache.h"
|
||||
|
||||
/** Allows to insert tokens, update priorities and generally control the cache.
|
||||
*/
|
||||
|
||||
template <class Token>
|
||||
class Controller {
|
||||
public:
|
||||
///should be private
|
||||
std::vector<Token *> tokens; //tokens waiting to be added
|
||||
bool quit; //gracefully terminate.
|
||||
bool paused;
|
||||
bool stopped;
|
||||
|
||||
public:
|
||||
///should be protected
|
||||
Provider<Token> provider;
|
||||
///should be protected
|
||||
std::vector<Cache<Token> *> caches;
|
||||
|
||||
Controller(): quit(false), paused(false), stopped(true) {}
|
||||
~Controller() { finish(); }
|
||||
|
||||
///called before the cache is started to add a cache in the chain
|
||||
/** The order in which the caches are added is from the lowest to the highest. */
|
||||
void addCache(Cache<Token> *cache) {
|
||||
if(caches.size() == 0)
|
||||
cache->setInputCache(&provider);
|
||||
else
|
||||
cache->setInputCache(caches.back());
|
||||
assert(cache->input);
|
||||
caches.push_back(cache);
|
||||
}
|
||||
///insert a token in the last provider (actual insertion is done on updatePriorities)
|
||||
void addToken(Token *token) {
|
||||
token->count = Token::CACHE;
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
///WARNING: migh stall for the time needed to drop tokens from cache.
|
||||
//FUNCTOR has bool operator(Token *) and return true to remove
|
||||
template<class FUNCTOR> void removeTokens(FUNCTOR functor) {
|
||||
stop();
|
||||
|
||||
std::vector<Token *> tmp;
|
||||
for(quint32 i = 0; i < caches.size(); i++)
|
||||
caches[i]->flush(functor);
|
||||
|
||||
provider.flush(functor);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
///if more tokens than m present in the provider, lowest priority ones will be removed
|
||||
void setMaxTokens(int m) {
|
||||
QMutexLocker l(&provider.heap_lock);
|
||||
provider.max_tokens = m;
|
||||
}
|
||||
|
||||
///ensure that added tokens are processed and existing ones have their priority updated.
|
||||
void updatePriorities() {
|
||||
|
||||
if(tokens.size()) {
|
||||
QMutexLocker l(&provider.heap_lock);
|
||||
for(unsigned int i = 0; i < tokens.size(); i++)
|
||||
provider.heap.push(tokens[i]);
|
||||
tokens.clear();
|
||||
}
|
||||
|
||||
provider.pushPriorities();
|
||||
for(unsigned int i = 0; i < caches.size(); i++)
|
||||
caches[i]->pushPriorities();
|
||||
}
|
||||
|
||||
///start the various cache threads.
|
||||
void start() {
|
||||
if(!stopped) return;
|
||||
assert(!paused);
|
||||
assert(caches.size() > 1);
|
||||
caches.back()->final = true;
|
||||
for(unsigned int i = 0; i < caches.size(); i++) //cache 0 is a provider, and his thread is not running.
|
||||
caches[i]->start();
|
||||
stopped = false;
|
||||
}
|
||||
///stops the ache threads
|
||||
void stop() {
|
||||
if(stopped) return;
|
||||
if(paused) resume();
|
||||
//stop threads
|
||||
for(int i = caches.size()-1; i >= 0; i--) {
|
||||
caches[i]->quit = true; //hmmmmmmmmmmmmmm not very clean.
|
||||
if(i == 0)
|
||||
provider.check_queue.open();
|
||||
else
|
||||
caches[i-1]->check_queue.open(); //cache i listens on queue i-1
|
||||
caches[i]->wait();
|
||||
}
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
void finish() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void pause() {
|
||||
if(paused) return;
|
||||
provider.heap_lock.lock();
|
||||
for(unsigned int i = 0; i < caches.size(); i++)
|
||||
caches[i]->heap_lock.lock();
|
||||
paused = true;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
if(!paused) return;
|
||||
provider.heap_lock.unlock();
|
||||
for(unsigned int i = 0; i < caches.size(); i++)
|
||||
caches[i]->heap_lock.unlock();
|
||||
paused = false;
|
||||
}
|
||||
///empty all caches
|
||||
void flush() {
|
||||
for(unsigned int i = caches.size()-1; i >= 0; i--)
|
||||
caches[i]->flush();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // CONTROLLER_H
|
|
@ -0,0 +1,274 @@
|
|||
/****************************************************************************
|
||||
* GCache *
|
||||
* Author: Federico Ponchio *
|
||||
* *
|
||||
* Copyright(C) 2011 *
|
||||
* Visual Computing Lab *
|
||||
* ISTI - Italian National Research Council *
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
||||
* for more details. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef DD_HEAP_H
|
||||
#define DD_HEAP_H
|
||||
|
||||
/**
|
||||
Double ended heap inspired by
|
||||
Min-Max Heaps and Generalized Priority Queues
|
||||
M. D. ATKINSON,J.-R. SACK, N. SANTORO,and T. STROTHOTTE
|
||||
|
||||
This structure allows for quick extraction of biggest and smaller item out of a set
|
||||
with linear reconstruction of the ordering.
|
||||
|
||||
DHeap exposes the public interface of vector. (push_back(), resize() etc.).
|
||||
|
||||
Compared to a stl heap, rebuild is 15% longer, extraction is 2x longer,
|
||||
but you get both min and max extraction in log(n) time.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
template <class T>
|
||||
class DHeap: public std::vector<T> {
|
||||
public:
|
||||
|
||||
void push(const T& elt) {
|
||||
push_back(elt);
|
||||
bubbleUp(this->size()-1);
|
||||
}
|
||||
|
||||
T &min() { return this->front(); } //root is smallest element
|
||||
|
||||
T popMin() {
|
||||
T elt = this->front();
|
||||
//move the last element to the root and
|
||||
this->front() = this->back();
|
||||
this->pop_back();
|
||||
//enforce minmax heap property
|
||||
trickleDownMin(0);
|
||||
return elt;
|
||||
}
|
||||
|
||||
//max is second element
|
||||
T &max() {
|
||||
if(this->size() == 1) return at(0);
|
||||
return at(1);
|
||||
}
|
||||
|
||||
T popMax() {
|
||||
int p = 1;
|
||||
if(this->size() == 1) p = 0;
|
||||
T elt = at(p);
|
||||
//max is replaced with last item.
|
||||
at(p) = this->back();
|
||||
this->pop_back();
|
||||
trickleDownMax(p); //enforce minmax heap property
|
||||
return elt;
|
||||
}
|
||||
|
||||
//just reinsert all elements
|
||||
void rebuild() {
|
||||
for(unsigned int i = 0; i < this->size(); i++)
|
||||
bubbleUp(i);
|
||||
}
|
||||
|
||||
protected:
|
||||
T &at(int n) { return std::vector<T>::at(n); }
|
||||
|
||||
int isMax(int e) const { return e & 1; }
|
||||
int parentMin(int i) const { return (((i+2)>>2)<<1) - 2; }
|
||||
int parentMax(int i) const { return (((i+2)>>2)<<1) - 1; }
|
||||
int leftChildMin(int i) const { return (((i+2)>>1)<<2) -2; }
|
||||
int leftChildMax(int i) const { return (((i+2)>>1)<<2) -1; }
|
||||
|
||||
void swap(int a, int b) { T tmp = at(a); at(a) = at(b); at(b) = tmp; }
|
||||
|
||||
//returns smallest elemennt of children intervals (or self if no children)
|
||||
int smallestChild(int i) {
|
||||
int l = leftChildMin(i);
|
||||
if(l >= this->size()) return i; //no children, return self
|
||||
|
||||
int r = l+2; //right child
|
||||
if(r < this->size() && at(r) < at(l))
|
||||
return r;
|
||||
return l;
|
||||
}
|
||||
//return biggest children or self if no children
|
||||
int greatestChild(int i) {
|
||||
int l = leftChildMax(i);
|
||||
if(l >= this->size()) return i; //no children, return self
|
||||
|
||||
int r = l+2; //right child
|
||||
if(r < this->size() && at(r) > at(l))
|
||||
return r;
|
||||
return l;
|
||||
}
|
||||
|
||||
//all stuff involving swaps could be optimized perofming circular swaps
|
||||
// but you mantain the code after :)
|
||||
void trickleDownMin(int i) {
|
||||
while(1) {
|
||||
|
||||
//find smallest child
|
||||
unsigned int m = leftChildMin(i);
|
||||
if(m >= this->size()) break;
|
||||
unsigned int r = m+2;
|
||||
if(r < this->size() && at(r) < at(m))
|
||||
m = r;
|
||||
|
||||
if(at(m) < at(i)) { //if child is smaller swap
|
||||
swap(i, m);
|
||||
i = m; //check swapped children
|
||||
} else //no swap? finish
|
||||
break;
|
||||
|
||||
m = i+1; //enforce order in interval
|
||||
if(m >= this->size()) break;
|
||||
if(at(m) < at(i))
|
||||
swap(i, m);
|
||||
}
|
||||
}
|
||||
|
||||
void trickleDownMax(int i) {
|
||||
while(1) {
|
||||
|
||||
//find greatest child
|
||||
unsigned int m = leftChildMax(i);
|
||||
if(m >= this->size()) break;
|
||||
unsigned int r = m+2;
|
||||
if(r < this->size() && at(r) > at(m))
|
||||
m = r;
|
||||
|
||||
if(at(m) > at(i)) {
|
||||
swap(i, m);
|
||||
i = m;
|
||||
} else
|
||||
break;
|
||||
|
||||
m = i-1; //enforce order in interval
|
||||
if(m >= this->size()) break;
|
||||
if(at(m) > at(i)) {
|
||||
swap(i, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bubbleUpMin(int i) {
|
||||
while(1) {
|
||||
int m = parentMin(i);
|
||||
if(m < 0) break;
|
||||
if(at(m) > at(i)) {
|
||||
swap(i, m);
|
||||
i = m;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bubbleUpMax(int i) {
|
||||
while(1) {
|
||||
int m = parentMax(i);
|
||||
if(m < 0) break;
|
||||
if(at(m) < at(i)) {
|
||||
swap(i, m);
|
||||
i = m;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bubbleUp(int i) {
|
||||
if(isMax(i)) {
|
||||
int m = i-1;
|
||||
if(at(m) > at(i)) {
|
||||
swap(i, m);
|
||||
bubbleUpMin(m);
|
||||
} else
|
||||
bubbleUpMax(i);
|
||||
} else {
|
||||
int m = parentMax(i);
|
||||
if(m < 0) return;
|
||||
if(at(m) < at(i)) {
|
||||
swap(i, m);
|
||||
bubbleUpMax(m);
|
||||
} else
|
||||
bubbleUpMin(i);//just reinsert all elements, (no push back necessary, of course
|
||||
}
|
||||
}
|
||||
/* DEBUG */
|
||||
public:
|
||||
///check the double heap conditions are met, mainly for debugging purpouses
|
||||
bool isHeap() { //checks everything is in order
|
||||
int s = this->size();
|
||||
for(int i = 0; i < s; i += 2) {
|
||||
if(i+1 < s && at(i) > at(i+1)) return false;
|
||||
int l = leftChildMin(i);
|
||||
if(l < s && at(i) > at(l)) return false;
|
||||
int r = l + 2;
|
||||
if(r < s && at(i) > at(r)) return false;
|
||||
}
|
||||
for(int i = 1; i < s; i += 2) {
|
||||
int l = leftChildMax(i);
|
||||
if(l < s && at(i) < at(l)) return false;
|
||||
int r = l + 2;
|
||||
if(r < s && at(i) < at(r)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Same functionality as IHeap, but storing pointers instead of the objects */
|
||||
|
||||
template <class T>
|
||||
class PtrDHeap {
|
||||
private:
|
||||
class Item {
|
||||
public:
|
||||
T *value;
|
||||
Item(T *val): value(val) {}
|
||||
bool operator<(const Item &i) const { return *value < *i.value; }
|
||||
bool operator>(const Item &i) const { return *value > *i.value; }
|
||||
};
|
||||
DHeap<Item> heap;
|
||||
|
||||
public:
|
||||
T *push(T *t) {
|
||||
Item i(t);
|
||||
heap.push(i);
|
||||
return i.value;
|
||||
}
|
||||
void push_back(T *t) {
|
||||
heap.push_back(Item(t));
|
||||
}
|
||||
int size() { return heap.size(); }
|
||||
void resize(int n) { assert(n < (int)heap.size()); return heap.resize(n, Item(NULL)); }
|
||||
void clear() { heap.clear(); }
|
||||
T &min() { Item &i = heap.min(); return *i.value; }
|
||||
T *popMin() { Item i = heap.popMin(); return i.value; }
|
||||
|
||||
T &max() { Item &i = heap.max(); return *i.value; }
|
||||
T *popMax() { Item i = heap.popMax(); return i.value; }
|
||||
|
||||
void rebuild() { heap.rebuild(); }
|
||||
T &operator[](int i) {
|
||||
return *(heap[i].value);
|
||||
}
|
||||
Item &at(int i) { return heap[i]; }
|
||||
bool isHeap() { return heap.isHeap(); }
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
|||
.str,.atv{color:#080}
|
||||
.kwd,.tag{color:#008}
|
||||
.com{color:#800}
|
||||
.typ,.atn,.dec{color:#606}
|
||||
.lit{color:#066}
|
||||
.pun{color:#660}
|
||||
.pln{color:#000}
|
||||
pre.prettyprint{padding:2px;border:1px solid #888}
|
||||
@media print{.str{color:#060}
|
||||
.kwd,.tag{color:#006;font-weight:bold}
|
||||
.com{color:#600;font-style:italic}
|
||||
.typ{font-weight:bold}
|
||||
.lit{color:#044}
|
||||
.pun{color:#440}
|
||||
.atn,.typ{color:#404}
|
||||
.atv{color:#060}}
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 114 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1,458 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="New document 1">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<inkscape:perspective
|
||||
id="perspective3624"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3669"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3726"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="328.49695"
|
||||
inkscape:cy="963.98253"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="739"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
x="56.568542"
|
||||
y="48.270554"
|
||||
id="text2816"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan2818"
|
||||
x="56.568542"
|
||||
y="48.270554" /></text>
|
||||
<g
|
||||
id="g3857">
|
||||
<text
|
||||
id="text3600"
|
||||
y="84.636047"
|
||||
x="309.30972"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
y="84.636047"
|
||||
x="309.30972"
|
||||
id="tspan3602"
|
||||
sodipodi:role="line">Capacity</tspan></text>
|
||||
<text
|
||||
id="text3604"
|
||||
y="84.636047"
|
||||
x="235.36554"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
y="84.636047"
|
||||
x="235.36554"
|
||||
id="tspan3606"
|
||||
sodipodi:role="line">Size</tspan></text>
|
||||
<path
|
||||
id="path3608"
|
||||
d="m 254.55844,105.84925 0,-17.772785"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="m 348.50269,105.84925 0,-17.772783"
|
||||
id="path3610" />
|
||||
<text
|
||||
id="text3612"
|
||||
y="123.36304"
|
||||
x="419.70758"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
xml:space="preserve"><tspan
|
||||
y="123.36304"
|
||||
x="419.70758"
|
||||
id="tspan3614"
|
||||
sodipodi:role="line">Load</tspan></text>
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1"
|
||||
id="g3786">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect2820"
|
||||
width="309.10672"
|
||||
height="23.233515"
|
||||
x="39.39595"
|
||||
y="105.84925" />
|
||||
<rect
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect3598"
|
||||
width="29.7995"
|
||||
height="23.233513"
|
||||
x="224.75897"
|
||||
y="105.84925" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="39.39595"
|
||||
height="23.233513"
|
||||
width="28.284269"
|
||||
id="rect3643"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="67.680222"
|
||||
height="23.233513"
|
||||
width="17.172592"
|
||||
id="rect3645"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="84.852814"
|
||||
height="23.233513"
|
||||
width="16.667517"
|
||||
id="rect3647"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="101.52033"
|
||||
height="23.233513"
|
||||
width="19.697973"
|
||||
id="rect3649"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="121.21831"
|
||||
height="23.233513"
|
||||
width="25.758888"
|
||||
id="rect3651"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="146.9772"
|
||||
height="23.233513"
|
||||
width="16.162447"
|
||||
id="rect3653"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="163.13965"
|
||||
height="23.233513"
|
||||
width="18.68784"
|
||||
id="rect3655"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="181.82748"
|
||||
height="23.233513"
|
||||
width="23.738588"
|
||||
id="rect3657"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="105.84925"
|
||||
x="205.56607"
|
||||
height="23.233513"
|
||||
width="19.192902"
|
||||
id="rect3659"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1"
|
||||
transform="translate(1.0921666,12)"
|
||||
id="g3814">
|
||||
<rect
|
||||
style="fill:#008000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect2820-3"
|
||||
width="68.185295"
|
||||
height="23.233521"
|
||||
x="279.22516"
|
||||
y="175.03113" />
|
||||
<rect
|
||||
style="fill:#800000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect3641"
|
||||
width="30.609148"
|
||||
height="23.233521"
|
||||
x="361.42856"
|
||||
y="175.03113" />
|
||||
<rect
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect3598-6"
|
||||
width="29.7995"
|
||||
height="23.233513"
|
||||
x="223.66682"
|
||||
y="175.03113" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="38.303783"
|
||||
height="23.233513"
|
||||
width="28.284269"
|
||||
id="rect3643-7"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="66.588058"
|
||||
height="23.233513"
|
||||
width="17.172592"
|
||||
id="rect3645-2"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="83.760651"
|
||||
height="23.233513"
|
||||
width="16.667517"
|
||||
id="rect3647-7"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="100.42818"
|
||||
height="23.233513"
|
||||
width="19.697973"
|
||||
id="rect3649-4"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="120.12614"
|
||||
height="23.233513"
|
||||
width="25.758888"
|
||||
id="rect3651-1"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="145.88504"
|
||||
height="23.233513"
|
||||
width="16.162447"
|
||||
id="rect3653-0"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="162.04749"
|
||||
height="23.233513"
|
||||
width="18.68784"
|
||||
id="rect3655-0"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="180.73532"
|
||||
height="23.233513"
|
||||
width="23.738588"
|
||||
id="rect3657-6"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="204.47391"
|
||||
height="23.233513"
|
||||
width="19.192902"
|
||||
id="rect3659-4"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="253.46632"
|
||||
height="23.233521"
|
||||
width="25.758841"
|
||||
id="rect3710"
|
||||
style="fill:#008000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="175.03113"
|
||||
x="308.0145"
|
||||
height="23.233521"
|
||||
width="53.414062"
|
||||
id="rect3712"
|
||||
style="fill:#d45500;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
</g>
|
||||
<text
|
||||
id="text3714"
|
||||
y="204.54492"
|
||||
x="419.18903"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
xml:space="preserve"><tspan
|
||||
y="204.54492"
|
||||
x="419.18903"
|
||||
id="tspan3716"
|
||||
sodipodi:role="line">Unload</tspan></text>
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1"
|
||||
transform="translate(0,6)"
|
||||
id="g3799">
|
||||
<rect
|
||||
style="fill:#008000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect2820-3-1"
|
||||
width="68.185295"
|
||||
height="23.233521"
|
||||
x="280.31732"
|
||||
y="141.08276" />
|
||||
<rect
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect3598-6-5"
|
||||
width="29.7995"
|
||||
height="23.233513"
|
||||
x="224.75899"
|
||||
y="141.08276" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="39.39595"
|
||||
height="23.233513"
|
||||
width="28.284269"
|
||||
id="rect3643-7-3"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="67.680222"
|
||||
height="23.233513"
|
||||
width="17.172592"
|
||||
id="rect3645-2-2"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="84.852821"
|
||||
height="23.233513"
|
||||
width="16.667517"
|
||||
id="rect3647-7-1"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="101.52034"
|
||||
height="23.233513"
|
||||
width="19.697973"
|
||||
id="rect3649-4-9"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="121.21831"
|
||||
height="23.233513"
|
||||
width="25.758888"
|
||||
id="rect3651-1-7"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="146.9772"
|
||||
height="23.233513"
|
||||
width="16.162447"
|
||||
id="rect3653-0-6"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="163.13965"
|
||||
height="23.233513"
|
||||
width="18.68784"
|
||||
id="rect3655-0-8"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="181.82748"
|
||||
height="23.233513"
|
||||
width="23.738588"
|
||||
id="rect3657-6-2"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="205.56607"
|
||||
height="23.233513"
|
||||
width="19.192902"
|
||||
id="rect3659-4-0"
|
||||
style="fill:#008000;fill-opacity:0.99555556000000001;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="254.55849"
|
||||
height="23.233521"
|
||||
width="25.758841"
|
||||
id="rect3710-6"
|
||||
style="fill:#008000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<rect
|
||||
y="141.08276"
|
||||
x="309.10669"
|
||||
height="23.233521"
|
||||
width="53.414062"
|
||||
id="rect3712-4"
|
||||
style="fill:#d45500;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path3830"
|
||||
d="m 348.50269,89.686811 0,139.401039"
|
||||
style="fill:#d45500;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||
<path
|
||||
style="fill:#d45500;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="m 39.396011,90.949502 0,139.401038"
|
||||
id="path3847"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
id="text3849"
|
||||
y="161.0282"
|
||||
x="419.28571"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
xml:space="preserve"><tspan
|
||||
y="161.0282"
|
||||
x="419.28571"
|
||||
id="tspan3851"
|
||||
sodipodi:role="line">Load if higher priority</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
|
||||
x="156.41992"
|
||||
y="41.647896"
|
||||
id="text3853"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3855"
|
||||
x="156.41992"
|
||||
y="41.647896"
|
||||
style="font-size:24px">Cache overflow management</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 248 B |
|
@ -0,0 +1,16 @@
|
|||
.str,.atv{color:#080}
|
||||
.kwd,.tag{color:#008}
|
||||
.com{color:#800}
|
||||
.typ,.atn,.dec{color:#606}
|
||||
.lit{color:#066}
|
||||
.pun{color:#660}
|
||||
.pln{color:#000}
|
||||
pre.prettyprint{padding:2px;border:1px solid #888}
|
||||
@media print{.str{color:#060}
|
||||
.kwd,.tag{color:#006;font-weight:bold}
|
||||
.com{color:#600;font-style:italic}
|
||||
.typ{font-weight:bold}
|
||||
.lit{color:#044}
|
||||
.pun{color:#440}
|
||||
.atn,.typ{color:#404}
|
||||
.atv{color:#060}}
|
|
@ -0,0 +1,46 @@
|
|||
window.PR_SHOULD_USE_CONTINUATION=true,window.PR_TAB_WIDTH=8,window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void
|
||||
0,window._pr_isIE6=function(){var a=navigator&&navigator.userAgent&&navigator.userAgent.match(/\bMSIE ([678])\./);return a=a?+a[1]:false,window._pr_isIE6=function(){return a},a},(function(){var
|
||||
a=true,b=null,c='break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof ',d=c+'alignof align_union asm axiom bool '+'concept concept_map const_cast constexpr decltype '+'dynamic_cast explicit export friend inline late_check '+'mutable namespace nullptr reinterpret_cast static_assert static_cast '+'template typeid typename using virtual wchar_t where ',e=c+'abstract boolean byte extends final finally implements import '+'instanceof null native package strictfp super synchronized throws '+'transient ',f=e+'as base by checked decimal delegate descending event '+'fixed foreach from group implicit in interface internal into is lock '+'object out override orderby params partial readonly ref sbyte sealed '+'stackalloc string select uint ulong unchecked unsafe ushort var ',g=c+'debugger eval export function get null set undefined var with '+'Infinity NaN ',h='caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ',i='break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ',j='break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ',k='break continue do else for if return while case done elif esac eval fi function in local set then until ',l=d+f+g+h+i+j+k,m=(function(){var
|
||||
a=['!','!=','!==','#','%','%=','&','&&','&&=','&=','(','*','*=','+=',',','-=','->','/','/=',':','::',';','<','<<','<<=','<=','=','==','===','>','>=','>>','>>=','>>>','>>>=','?','@','[','^','^=','^^','^^=','{','|','|=','||','||=','~','break','case','continue','delete','do','else','finally','instanceof','return','throw','try','typeof'],b='(?:^^|[+-]',c;for(c=0;c<a.length;++c)b+='|'+a[c].replace(/([^=<>:&a-z])/g,'\\$1');return b+=')\\s*',b})(),n=/&/g,o=/</g,p=/>/g,q=/\"/g,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F;function
|
||||
G(a){return a.replace(n,'&').replace(o,'<').replace(p,'>').replace(q,'"')}function
|
||||
H(a){return a.replace(n,'&').replace(o,'<').replace(p,'>')}C=/</g,B=/>/g,w=/'/g,E=/"/g,v=/&/g,D=/ /g;function
|
||||
I(a){var b=a.indexOf('&'),c,d,e,f;if(b<0)return a;for(--b;(b=a.indexOf('&#',b+1))>=0;)d=a.indexOf(';',b),d>=0&&(e=a.substring(b+3,d),f=10,e&&e.charAt(0)==='x'&&(e=e.substring(1),f=16),c=parseInt(e,f),isNaN(c)||(a=a.substring(0,b)+String.fromCharCode(c)+a.substring(d+1)));return a.replace(C,'<').replace(B,'>').replace(w,'\'').replace(E,'\"').replace(D,' ').replace(v,'&')}function
|
||||
J(a){return'XMP'===a.tagName}u=/[\r\n]/g;function K(c,d){var e;return'PRE'===c.tagName?a:u.test(d)?(e='',c.currentStyle?(e=c.currentStyle.whiteSpace):window.getComputedStyle&&(e=window.getComputedStyle(c,b).whiteSpace),!e||e==='pre'):a}function
|
||||
L(a,b){var c,d,e,f;switch(a.nodeType){case 1:f=a.tagName.toLowerCase(),b.push('<',f);for(e=0;e<a.attributes.length;++e){c=a.attributes[e];if(!c.specified)continue;b.push(' '),L(c,b)}b.push('>');for(d=a.firstChild;d;d=d.nextSibling)L(d,b);(a.firstChild||!/^(?:br|link|img)$/.test(f))&&b.push('</',f,'>');break;case
|
||||
2:b.push(a.name.toLowerCase(),'=\"',G(a.value),'\"');break;case 3:case 4:b.push(H(a.nodeValue))}}function
|
||||
M(b){var c=0,d=false,e=false,f,g,h,i;for(f=0,g=b.length;f<g;++f){h=b[f];if(h.ignoreCase)e=a;else
|
||||
if(/[a-z]/i.test(h.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,''))){d=a,e=false;break}}function
|
||||
j(a){if(a.charAt(0)!=='\\')return a.charCodeAt(0);switch(a.charAt(1)){case'b':return 8;case't':return 9;case'n':return 10;case'v':return 11;case'f':return 12;case'r':return 13;case'u':case'x':return parseInt(a.substring(2),16)||a.charCodeAt(1);case'0':case'1':case'2':case'3':case'4':case'5':case'6':case'7':return parseInt(a.substring(1),8);default:return a.charCodeAt(1)}}function
|
||||
k(a){var b;return a<32?(a<16?'\\x0':'\\x')+a.toString(16):(b=String.fromCharCode(a),(b==='\\'||b==='-'||b==='['||b===']')&&(b='\\'+b),b)}function
|
||||
l(a){var b=a.substring(1,a.length-1).match(new RegExp('\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]','g')),c=[],d=[],e=b[0]==='^',f,g,h,i,m,n,o,p,q;for(h=e?1:0,m=b.length;h<m;++h){o=b[h];switch(o){case'\\B':case'\\b':case'\\D':case'\\d':case'\\S':case'\\s':case'\\W':case'\\w':c.push(o);continue}q=j(o),h+2<m&&'-'===b[h+1]?(g=j(b[h+2]),h+=2):(g=q),d.push([q,g]),g<65||q>122||(g<65||q>90||d.push([Math.max(65,q)|32,Math.min(g,90)|32]),g<97||q>122||d.push([Math.max(97,q)&-33,Math.min(g,122)&-33]))}d.sort(function(a,b){return a[0]-b[0]||b[1]-a[1]}),f=[],i=[NaN,NaN];for(h=0;h<d.length;++h)p=d[h],p[0]<=i[1]+1?(i[1]=Math.max(i[1],p[1])):f.push(i=p);n=['['],e&&n.push('^'),n.push.apply(n,c);for(h=0;h<f.length;++h)p=f[h],n.push(k(p[0])),p[1]
|
||||
>p[0]&&(p[1]+1>p[0]&&n.push('-'),n.push(k(p[1])));return n.push(']'),n.join('')}function
|
||||
m(a){var b=a.source.match(new RegExp('(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)','g')),e=b.length,f=[],g,h,i,j,k;for(j=0,i=0;j<e;++j)k=b[j],k==='('?++i:'\\'===k.charAt(0)&&(h=+k.substring(1),h&&h<=i&&(f[h]=-1));for(j=1;j<f.length;++j)-1===f[j]&&(f[j]=++c);for(j=0,i=0;j<e;++j)k=b[j],k==='('?(++i,f[i]===void
|
||||
0&&(b[j]='(?:')):'\\'===k.charAt(0)&&(h=+k.substring(1),h&&h<=i&&(b[j]='\\'+f[i]));for(j=0,i=0;j<e;++j)'^'===b[j]&&'^'!==b[j+1]&&(b[j]='');if(a.ignoreCase&&d)for(j=0;j<e;++j)k=b[j],g=k.charAt(0),k.length>=2&&g==='['?(b[j]=l(k)):g!=='\\'&&(b[j]=k.replace(/[a-zA-Z]/g,function(a){var
|
||||
b=a.charCodeAt(0);return'['+String.fromCharCode(b&-33,b|32)+']'}));return b.join('')}i=[];for(f=0,g=b.length;f<g;++f){h=b[f];if(h.global||h.multiline)throw new
|
||||
Error(''+h);i.push('(?:'+m(h)+')')}return new RegExp(i.join('|'),e?'gi':'g')}r=b;function
|
||||
N(a){var c,d,e,f;b===r&&(f=document.createElement('PRE'),f.appendChild(document.createTextNode('<!DOCTYPE foo PUBLIC \"foo bar\">\n<foo />')),r=!/</.test(f.innerHTML));if(r)return d=a.innerHTML,J(a)?(d=H(d)):K(a,d)||(d=d.replace(/(<br\s*\/?>)[\r\n]+/g,'$1').replace(/(?:[\r\n]+[ \t]*)+/g,' ')),d;e=[];for(c=a.firstChild;c;c=c.nextSibling)L(c,e);return e.join('')}function
|
||||
O(a){var c=0;return function(d){var e=b,f=0,g,h,i,j;for(h=0,i=d.length;h<i;++h){g=d.charAt(h);switch(g){case' ':e||(e=[]),e.push(d.substring(f,h)),j=a-c%a,c+=j;for(;j>=0;j-=' '.length)e.push(' '.substring(0,j));f=h+1;break;case'\n':c=0;break;default:++c}}return e?(e.push(d.substring(f)),e.join('')):d}}z=new
|
||||
RegExp('[^<]+|<!--[\\s\\S]*?-->|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|</?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>|<','g'),A=/^<\!--/,y=/^<!\[CDATA\[/,x=/^<br\b/i,F=/^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;function
|
||||
P(a){var b=a.match(z),c=[],d=0,e=[],f,g,h,i,j,k,l,m;if(b)for(g=0,k=b.length;g<k;++g){j=b[g];if(j.length>1&&j.charAt(0)==='<'){if(A.test(j))continue;if(y.test(j))c.push(j.substring(9,j.length-3)),d+=j.length-12;else
|
||||
if(x.test(j))c.push('\n'),++d;else if(j.indexOf('nocode')>=0&&Q(j)){l=(j.match(F))[2],f=1;for(h=g+1;h<k;++h){m=b[h].match(F);if(m&&m[2]===l)if(m[1]==='/'){if(--f===0)break}else++f}h<k?(e.push(d,b.slice(g,h+1).join('')),g=h):e.push(d,j)}else
|
||||
e.push(d,j)}else i=I(j),c.push(i),d+=i.length}return{source:c.join(''),tags:e}}function
|
||||
Q(a){return!!a.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,' $1=\"$2$3$4\"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)}function
|
||||
R(a,b,c,d){var e;if(!b)return;e={source:b,basePos:a},c(e),d.push.apply(d,e.decorations)}function
|
||||
S(a,c){var d={},e,f,g,h;return(function(){var e=a.concat(c),f=[],g={},i,j,k,l,m,n,o;for(j=0,l=e.length;j<l;++j){m=e[j],o=m[3];if(o)for(i=o.length;--i>=0;)d[o.charAt(i)]=m;n=m[1],k=''+n,g.hasOwnProperty(k)||(f.push(n),g[k]=b)}f.push(/[\0-\uffff]/),h=M(f)})(),f=c.length,g=/\S/,e=function(a){var
|
||||
b=a.source,g=a.basePos,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y;i=[g,'pln'],s=0,y=b.match(h)||[],u={};for(v=0,q=y.length;v<q;++v){w=y[v],t=u[w],p=void
|
||||
0;if(typeof t==='string')n=false;else{r=d[w.charAt(0)];if(r)p=w.match(r[1]),t=r[0];else{for(m=0;m<f;++m){r=c[m],p=w.match(r[1]);if(p){t=r[0];break}}p||(t='pln')}n=t.length>=5&&'lang-'===t.substring(0,5),n&&!(p&&typeof
|
||||
p[1]==='string')&&(n=false,t='src'),n||(u[w]=t)}x=s,s+=w.length,n?(j=p[1],l=w.indexOf(j),k=l+j.length,p[2]&&(k=w.length-p[2].length,l=k-j.length),o=t.substring(5),R(g+x,w.substring(0,l),e,i),R(g+x+l,j,W(o,j),i),R(g+x+k,w.substring(k),e,i)):i.push(g+x,t)}a.decorations=i},e}function
|
||||
T(a){var c=[],d=[],e,f;return a.tripleQuotedStrings?c.push(['str',/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,b,'\'\"']):a.multiLineStrings?c.push(['str',/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,b,'\'\"`']):c.push(['str',/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,b,'\"\'']),a.verbatimStrings&&d.push(['str',/^@\"(?:[^\"]|\"\")*(?:\"|$)/,b]),a.hashComments&&(a.cStyleComments?(c.push(['com',/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,b,'#']),d.push(['str',/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,b])):c.push(['com',/^#[^\r\n]*/,b,'#'])),a.cStyleComments&&(d.push(['com',/^\/\/[^\r\n]*/,b]),d.push(['com',/^\/\*[\s\S]*?(?:\*\/|$)/,b])),a.regexLiterals&&(e='/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/',d.push(['lang-regex',new
|
||||
RegExp('^'+m+'('+e+')')])),f=a.keywords.replace(/^\s+|\s+$/g,''),f.length&&d.push(['kwd',new
|
||||
RegExp('^(?:'+f.replace(/\s+/g,'|')+')\\b'),b]),c.push(['pln',/^\s+/,b,' \r\n \xa0']),d.push(['lit',/^@[a-z_$][a-z_$@0-9]*/i,b],['typ',/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,b],['pln',/^[a-z_$][a-z_$@0-9]*/i,b],['lit',new
|
||||
RegExp('^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*','i'),b,'0123456789'],['pun',/^.[^\s\w\.$@\'\"\`\/\#]*/,b]),S(c,d)}s=T({keywords:l,hashComments:a,cStyleComments:a,multiLineStrings:a,regexLiterals:a});function
|
||||
U(c){var d=c.source,e=c.extractedTags,f=c.decorations,g=[],h=0,i=b,j=b,k=0,l=0,m=O(window.PR_TAB_WIDTH),n=/([\r\n ]) /g,o=/(^| ) /gm,p=/\r\n?|\n/g,q=/[ \r\n]$/,r=a,s;function
|
||||
t(a){var c,e;a>h&&(i&&i!==j&&(g.push('</span>'),i=b),!i&&j&&(i=j,g.push('<span class=\"',i,'\">')),c=H(m(d.substring(h,a))).replace(r?o:n,'$1 '),r=q.test(c),e=window._pr_isIE6()?' <br />':'<br />',g.push(c.replace(p,e)),h=a)}while(a){k<e.length?l<f.length?(s=e[k]<=f[l]):(s=a):(s=false);if(s)t(e[k]),i&&(g.push('</span>'),i=b),g.push(e[k+1]),k+=2;else
|
||||
if(l<f.length)t(f[l]),j=f[l+1],l+=2;else break}t(d.length),i&&g.push('</span>'),c.prettyPrintedHtml=g.join('')}t={};function
|
||||
V(a,b){var c,d;for(d=b.length;--d>=0;)c=b[d],t.hasOwnProperty(c)?'console'in window&&console.warn('cannot override language handler %s',c):(t[c]=a)}function
|
||||
W(a,b){return a&&t.hasOwnProperty(a)||(a=/^\s*</.test(b)?'default-markup':'default-code'),t[a]}V(s,['default-code']),V(S([],[['pln',/^[^<?]+/],['dec',/^<!\w[^>]*(?:>|$)/],['com',/^<\!--[\s\S]*?(?:-\->|$)/],['lang-',/^<\?([\s\S]+?)(?:\?>|$)/],['lang-',/^<%([\s\S]+?)(?:%>|$)/],['pun',/^(?:<[%?]|[%?]>)/],['lang-',/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],['lang-js',/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],['lang-css',/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],['lang-in.tag',/^(<\/?[a-z][^<>]*>)/i]]),['default-markup','htm','html','mxml','xhtml','xml','xsl']),V(S([['pln',/^[\s]+/,b,' \r\n'],['atv',/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,b,'\"\'']],[['tag',/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],['atn',/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],['lang-uq.val',/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],['pun',/^[=<>\/]+/],['lang-js',/^on\w+\s*=\s*\"([^\"]+)\"/i],['lang-js',/^on\w+\s*=\s*\'([^\']+)\'/i],['lang-js',/^on\w+\s*=\s*([^\"\'>\s]+)/i],['lang-css',/^style\s*=\s*\"([^\"]+)\"/i],['lang-css',/^style\s*=\s*\'([^\']+)\'/i],['lang-css',/^style\s*=\s*([^\"\'>\s]+)/i]]),['in.tag']),V(S([],[['atv',/^[\s\S]+/]]),['uq.val']),V(T({keywords:d,hashComments:a,cStyleComments:a}),['c','cc','cpp','cxx','cyc','m']),V(T({keywords:'null true false'}),['json']),V(T({keywords:f,hashComments:a,cStyleComments:a,verbatimStrings:a}),['cs']),V(T({keywords:e,cStyleComments:a}),['java']),V(T({keywords:k,hashComments:a,multiLineStrings:a}),['bsh','csh','sh']),V(T({keywords:i,hashComments:a,multiLineStrings:a,tripleQuotedStrings:a}),['cv','py']),V(T({keywords:h,hashComments:a,multiLineStrings:a,regexLiterals:a}),['perl','pl','pm']),V(T({keywords:j,hashComments:a,multiLineStrings:a,regexLiterals:a}),['rb']),V(T({keywords:g,cStyleComments:a,regexLiterals:a}),['js']),V(S([],[['str',/^[\s\S]+/]]),['regex']);function
|
||||
X(a){var b=a.sourceCodeHtml,c=a.langExtension,d,e;a.prettyPrintedHtml=b;try{e=P(b),d=e.source,a.source=d,a.basePos=0,a.extractedTags=e.tags,W(c,d)(a),U(a)}catch(f){'console'in
|
||||
window&&(console.log(f),console.trace())}}function Y(a,b){var c={sourceCodeHtml:a,langExtension:b};return X(c),c.prettyPrintedHtml}function
|
||||
Z(c){var d=window._pr_isIE6(),e=d===6?'\r\n':'\r',f=[document.getElementsByTagName('pre'),document.getElementsByTagName('code'),document.getElementsByTagName('xmp')],g=[],h,i,j,k,l,m;for(i=0;i<f.length;++i)for(j=0,l=f[i].length;j<l;++j)g.push(f[i][j]);f=b,h=Date,h.now||(h={now:function(){return(new
|
||||
Date).getTime()}}),k=0;function n(){var b=window.PR_SHOULD_USE_CONTINUATION?h.now()+250:Infinity,d,e,f,i,j;for(;k<g.length&&h.now()<b;++k){e=g[k];if(e.className&&e.className.indexOf('prettyprint')>=0){f=e.className.match(/\blang-(\w+)\b/),f&&(f=f[1]),i=false;for(j=e.parentNode;j;j=j.parentNode)if((j.tagName==='pre'||j.tagName==='code'||j.tagName==='xmp')&&j.className&&j.className.indexOf('prettyprint')>=0){i=a;break}i||(d=N(e),d=d.replace(/(?:\r\n?|\n)$/,''),m={sourceCodeHtml:d,langExtension:f,sourceNode:e},X(m),o())}}k<g.length?setTimeout(n,250):c&&c()}function
|
||||
o(){var a=m.prettyPrintedHtml,b,c,f,g,h,i,j,k;if(!a)return;f=m.sourceNode;if(!J(f))f.innerHTML=a;else{k=document.createElement('PRE');for(g=0;g<f.attributes.length;++g)b=f.attributes[g],b.specified&&(c=b.name.toLowerCase(),c==='class'?(k.className=b.value):k.setAttribute(b.name,b.value));k.innerHTML=a,f.parentNode.replaceChild(k,f),f=k}if(d&&f.tagName==='PRE'){j=f.getElementsByTagName('br');for(h=j.length;--h>=0;)i=j[h],i.parentNode.replaceChild(document.createTextNode(e),i)}}n()}window.PR_normalizedHtml=L,window.prettyPrintOne=Y,window.prettyPrint=Z,window.PR={combinePrefixPatterns:M,createSimpleLexer:S,registerLangHandler:V,sourceDecorator:T,PR_ATTRIB_NAME:'atn',PR_ATTRIB_VALUE:'atv',PR_COMMENT:'com',PR_DECLARATION:'dec',PR_KEYWORD:'kwd',PR_LITERAL:'lit',PR_NOCODE:'nocode',PR_PLAIN:'pln',PR_PUNCTUATION:'pun',PR_SOURCE:'src',PR_STRING:'str',PR_TAG:'tag',PR_TYPE:'typ'}})()
|
|
@ -0,0 +1,175 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/prettify.css" type="text/css" media="all"/>
|
||||
<script type="text/javascript" src="js/prettify.js"></script>
|
||||
|
||||
<style>
|
||||
body { background-color:#fff; color: #555; font-family:Verdana; font-size:14px; }
|
||||
h1, h2, h3 { color:#333; font-family:Trebuchet MS}
|
||||
a:link, a:visited { text-decoration:none; color:#a00; }
|
||||
a:hover { text-decoration:none; color:#e00; }
|
||||
|
||||
.container { width:700px; margin:auto; font-size:90%;}
|
||||
.comment { color:#070; }
|
||||
pre.prettyprint { border:1px solid #ddd;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body onload="prettyPrint()">
|
||||
<div class="container" id="page">
|
||||
<h1>Generic cache system</h1>
|
||||
<h2>Overview</h2>
|
||||
<p>
|
||||
GCache is a generic multilevel priority based cache system which is useful for allocating resources
|
||||
across RAM, GPU, disk, network etc.</p>
|
||||
|
||||
<p><a href="html/index.html">Class documentation</a> created using Doxyigen is available in the <em>docs/html/</em> directory</p>
|
||||
<p>A quick example might better explain the concept; see <a href="#example">a more detailed example</a>
|
||||
for source code and in depth comments. we want to implement a priority cache for our
|
||||
database of 10000 images. We will use 2 levels of caching, RAM and GPU while the images will be stored on disk.</p>
|
||||
|
||||
<ul>
|
||||
<li>Each image is managed through a <em>token</em> which holds the pointer to the data and is used to assign a priority
|
||||
to the image.</li>
|
||||
<li>We subclass <em>Cache</em> to our RamCache and GpuCache and define function for loading from disk and
|
||||
uploading the texture to the GPU</li>
|
||||
<li>We can set priorities for the images according to our strategy.</li>
|
||||
<li>Before rendering the image we lock the token and unlock it when done</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<h2>Design</h2>
|
||||
<p>GCache is designed for very frequent priority updates where the majority of
|
||||
resurces are involved. All the system is designed to minimize the computations
|
||||
required to change priorities and sorting them.</p>
|
||||
|
||||
<div style="text-align:center; margin-bottom:20px;">
|
||||
<img src="img/architecture.png">
|
||||
</div>
|
||||
|
||||
<h3>Token</h3>
|
||||
<p>Each resource is managed through a pointer to a subclass of <code>Token<Priority></code>.
|
||||
Token pointers are transferred between caches but never created or deleted by the cache system.
|
||||
The user is responsible for storage of the tokens. </p>
|
||||
<p>
|
||||
Adding tokens to the cache is done using the function <code>Controller::addToken(Token *)</code>;
|
||||
the tokens can be scheduled for removal using the function <code>Token::remove()</code> or be dropped
|
||||
when the max number of tokens in the controller is reached
|
||||
as set by <code>Controller::setMaxTokens(int)</code>. Use <code>Token::isInCache()</code> to check
|
||||
for actual removal.</p>
|
||||
|
||||
|
||||
<h3>Priority</h3>
|
||||
<p>The <code>Token</code> class is templated over a <code>typename Priority</code> which usually
|
||||
it is a floating point number, but it can be anything sortable through <code>Priority::operator<(const Priority &)</code>.
|
||||
The goal of the cache system is to juggle resources around so to have the highest priority tokens in the higher cache.</p>
|
||||
|
||||
<p>Use function <code>Token::setPriority(Priority)</code> to set tokens priority; it does not require mutexing and it is
|
||||
therefore vert fast. The change will take effect only upon a call to <code>Controller::updatePriorities()</code>.</p>
|
||||
|
||||
<p>Each cache holds a double heap of Tokens (see architecture picture), 'sorted' accordingly to priority. A double heap is a data structure with similar
|
||||
properties to a heap but which allows quick extraction of both min and max element.</p>
|
||||
|
||||
<p>Priorities are sorted with a lazy approach, only when needed (usually when checking if a transfer should be performed. This
|
||||
results in the higher, smaller caches being sorted more frequently than the larger lower caches.</p>
|
||||
|
||||
|
||||
<h3>Caches</h3>
|
||||
|
||||
<p>The user needs to subclass the <code>Cache</code> class and override the virtual functions <code>get(Token *),
|
||||
drop(Token *) and size(Token *)</code>. Each cache runs its own thread. Resource transfers (reading a file content, uploading a texture, etc)
|
||||
are performed in that thread using blocking calls.</p>
|
||||
|
||||
<p>Cache are added to the controller using the function <code>Controller::addCache(Cache *)</code></p>
|
||||
|
||||
|
||||
<p>The GCache employs a nested cache model: if a resource is present in a cache is also present in all the lower ones,
|
||||
and thus each cache should have more capacity than the lower one. For sake of simplicity and performances each cache
|
||||
is allowed to overflow its capacity by at most one resource. (see picture)</p>
|
||||
|
||||
<div style="text-align:center; margin-bottom:20px;">
|
||||
<img src="img/overflow.png">
|
||||
</div>
|
||||
|
||||
<h3>Locking</h3>
|
||||
<p>Resources must be locked before use, to prevent cache threads to unload them while in use. An object can be locked
|
||||
only if already loaded in the highest cache, otherwise the <code>lock()</code> function will return false.</p>
|
||||
<p>Unlock allows the resource to be eventually dropped from the cache. Locks are recursive which means Tokens keep track
|
||||
of how many time <code>lock()</code> have been called and release the resource only after <code>unlock()</code>
|
||||
is called that many times.</p>
|
||||
<p>Loking and unlocking costs basically zero (we are using <code>QAtomicInt</code>).</p>
|
||||
|
||||
|
||||
<h2><a name="example"></a>Minimal example</h2>
|
||||
<p>This is a minimal pseudo-code example, just to quickly cover the basis. For a real example, involving
|
||||
also sending resources to the GPU using a share context check the <em>example/my_widget.h</em> file.
|
||||
</p>
|
||||
|
||||
<pre class="prettyprint">
|
||||
class MyToken: public Token<float> {
|
||||
public:
|
||||
MyData *data;
|
||||
GLUint texture;
|
||||
};
|
||||
|
||||
class RamCache: public Cache<MyToken> {
|
||||
public:
|
||||
//return size of object. return -1 on error
|
||||
int get(MyToken *token) { fread(someting into something); return 1; }
|
||||
//return size of objecy, cannot fail
|
||||
int drop(MyToken *token) { delete memory; return 1; }
|
||||
int size(MyToken *token) { return 1; }
|
||||
};
|
||||
|
||||
class GpuCache: public Cache<MyToken> {
|
||||
public:
|
||||
int get(MyToken *token) { glCreate + glTexture;return 1; }
|
||||
int drop(MyToken *token) { glDelete; return 1; }
|
||||
int size(MyToken *token) { return 1; }
|
||||
};
|
||||
|
||||
//generate tokens
|
||||
vector<MyToken> tokens;
|
||||
for(int i = 0; i < 10000; i++)
|
||||
tokens.push_back(MyToken(i));
|
||||
|
||||
//create ram and gpu caches
|
||||
RamCache ram;
|
||||
ram.setCapacity(5000);
|
||||
GpuCache gpu;
|
||||
gpu.setCapacity(1000);
|
||||
|
||||
//create controller and add caches
|
||||
Controller<MyToken> controller;
|
||||
controller.addCache(&ram);
|
||||
controller.addCache(&gpu);
|
||||
|
||||
//tell controller about tokens and start
|
||||
for(int i = 0; i < tokens.size(); i++) {
|
||||
tokens[i].setPriority(rand()/((double)RAND_MAX));
|
||||
controller.addToken(&tokens[i]);
|
||||
}
|
||||
|
||||
|
||||
controller.start();
|
||||
|
||||
//change priorities
|
||||
for(int i = 0; i < tokens.size(); i++)
|
||||
tokens[i].setPriority(rand());
|
||||
controller.updatePriorities();
|
||||
|
||||
//lock and use the tokens
|
||||
for(int i = 0; i < tokens.size(); i++) {
|
||||
bool success = tokens[i].lock();
|
||||
if(success) {
|
||||
//use the token as you see fit
|
||||
tokens[i].unlock();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,101 @@
|
|||
/****************************************************************************
|
||||
* GCache *
|
||||
* Author: Federico Ponchio *
|
||||
* *
|
||||
* Copyright(C) 2011 *
|
||||
* Visual Computing Lab *
|
||||
* ISTI - Italian National Research Council *
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
||||
* for more details. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef CACHE_DOOR_H
|
||||
#define CACHE_DOOR_H
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
//a door needs to be open for the thread to continue,
|
||||
//if it is open the thread enter and closes the door
|
||||
//this mess is to avoid [if(!open.available()) open.release(1)]
|
||||
#include <QSemaphore>
|
||||
class QDoor {
|
||||
private:
|
||||
QSemaphore _open;
|
||||
QSemaphore _close;
|
||||
public:
|
||||
QDoor(): _open(0), _close(1) {} //this means closed
|
||||
void open() {
|
||||
if(_close.tryAcquire(1)) //check it is not open
|
||||
_open.release(1); //open
|
||||
}
|
||||
void close() {
|
||||
if(_open.tryAcquire(1)) //check not already cloed
|
||||
_close.release(1);
|
||||
}
|
||||
void enter(bool close = false) {
|
||||
_open.acquire(1);
|
||||
if(close)
|
||||
_close.release(1); //and close door behind
|
||||
else
|
||||
_open.release(1); //and leave door opened
|
||||
}
|
||||
bool isOpen() { return _open.available() == 1; }
|
||||
};
|
||||
|
||||
*/
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
/**
|
||||
A wait condition class that works as a door.
|
||||
Should check if the semaphore version is faster.
|
||||
*/
|
||||
|
||||
class QDoor {
|
||||
public:
|
||||
|
||||
QDoor(void) : doorOpen(false) {}
|
||||
|
||||
///opens the door. Threads trying to enter will be awakened
|
||||
void open(void) {
|
||||
this->m.lock();
|
||||
this->doorOpen = true;
|
||||
this->m.unlock();
|
||||
this->c.wakeAll();
|
||||
}
|
||||
|
||||
///attempt to enter the door. if the door is closed the thread will wait until the door is opened.
|
||||
/** if close is true, the door will be closed after the thread is awakened, this allows to
|
||||
have only one thread entering the door each time open() is called */
|
||||
void enter(bool close = false) {
|
||||
this->m.lock();
|
||||
while (!this->doorOpen)
|
||||
this->c.wait(&(this->m));
|
||||
|
||||
if(close)
|
||||
this->doorOpen = false;
|
||||
this->m.unlock();
|
||||
}
|
||||
private:
|
||||
QMutex m;
|
||||
QWaitCondition c;
|
||||
bool doorOpen;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef GCACHE_PROVIDER_H
|
||||
#define GCACHE_PROVIDER_H
|
||||
|
||||
|
||||
#include <QMutex>
|
||||
#include "dheap.h"
|
||||
#include "door.h"
|
||||
|
||||
#include "token.h"
|
||||
|
||||
/* this cache system enforce the rule that the items in a cache are always in all the cache below */
|
||||
/* two mechanism to remove tokens from the cache:
|
||||
1) set token count to something low
|
||||
2) set maximum number of tokens in the provider
|
||||
*/
|
||||
|
||||
/** Base class for Cache and last cache in the GCache system.
|
||||
You should never interact with this class.
|
||||
*/
|
||||
|
||||
template <typename Token>
|
||||
class Provider: public QThread {
|
||||
public:
|
||||
///holds the resources in this cache but not in the cache above
|
||||
PtrDHeap<Token> heap;
|
||||
///tokens above this number will be scheduled for deletion
|
||||
int max_tokens;
|
||||
///signals we need to rebuild heap.
|
||||
bool heap_dirty;
|
||||
///lock this before manipulating heap.
|
||||
QMutex heap_lock;
|
||||
///used to sincronize priorities update
|
||||
QMutex priority_lock;
|
||||
///signals (to next cache!) priorities have changed or something is available
|
||||
QDoor check_queue;
|
||||
|
||||
Provider(): max_tokens(-1), heap_dirty(false) {}
|
||||
virtual ~Provider() {}
|
||||
|
||||
/// [should be protected, do not use]
|
||||
void pushPriorities() {
|
||||
QMutexLocker locker(&priority_lock);
|
||||
for(int i = 0; i < heap.size(); i++)
|
||||
heap[i].pushPriority();
|
||||
heap_dirty = true;
|
||||
check_queue.open();
|
||||
}
|
||||
/// assumes heap lock is locked, runs in cache thread [should be protected, do not use]
|
||||
void rebuild() {
|
||||
if(!this->heap_dirty) return;
|
||||
|
||||
{
|
||||
QMutexLocker locker(&priority_lock);
|
||||
for(int i = 0; i < this->heap.size(); i++)
|
||||
this->heap[i].pullPriority();
|
||||
this->heap_dirty = false;
|
||||
}
|
||||
this->heap.rebuild();
|
||||
|
||||
//remove OUTSIDE tokens from bottom of heap
|
||||
if(max_tokens != -1) {
|
||||
while(this->heap.size() > max_tokens) {
|
||||
Token &t = this->heap.min();
|
||||
t.count = Token::OUTSIDE;
|
||||
this->heap.popMin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///ensure no locked item are to be removed [should be protected, do not use]
|
||||
template <class FUNCTOR> void flush(FUNCTOR functor) {
|
||||
int count = 0;
|
||||
QMutexLocker locker(&(this->heap_lock));
|
||||
for(int k = 0; k < this->heap.size(); k++) {
|
||||
Token *token = &this->heap[k];
|
||||
if(functor(token)) { //drop it
|
||||
token->count = Token::OUTSIDE;
|
||||
} else
|
||||
this->heap.at(count++) = token;
|
||||
}
|
||||
this->heap.resize(count);
|
||||
this->heap_dirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef GCACHE_TOKEN_H
|
||||
#define GCACHE_TOKEN_H
|
||||
|
||||
#include <QAtomicInt>
|
||||
|
||||
/* QAtomic int count keep trak of token status:
|
||||
>0: locked (possibly multiple times)
|
||||
0: data ready in last cache
|
||||
-1: not in last cache
|
||||
-2: to removed from all caches
|
||||
-3: out of caches
|
||||
*/
|
||||
|
||||
/** Holds the resources to be cached.
|
||||
The Priority template argument can simply be a floating point number
|
||||
or something more complex, (frame and error in pixel); the only
|
||||
requirement is the existence of a < comparison operator */
|
||||
|
||||
template <typename Priority>
|
||||
class Token {
|
||||
public:
|
||||
///Resource loading status
|
||||
/*** - LOCKED: resource in the higher cache and locked
|
||||
- READY: resource in the higher cache
|
||||
- CACHE: resource in some cache (not the highest)
|
||||
- REMOVE: resource in some cache and scheduled for removal
|
||||
- OUTSIDE: resource not in the cache system */
|
||||
enum Status { LOCKED = 1, READY = 0, CACHE = -1, REMOVE = -2, OUTSIDE = -3 };
|
||||
///Do not access these members directly. Will be moved to private shortly.
|
||||
///used by various cache threads to sort objects [do not use, should be private]
|
||||
Priority priority;
|
||||
///set in the main thread [do not use, should be private]
|
||||
Priority new_priority;
|
||||
///swap space used in updatePriorities [do not use, should be private]
|
||||
Priority tmp_priority;
|
||||
///reference count of locked items [do not use, should be private]
|
||||
QAtomicInt count;
|
||||
|
||||
public:
|
||||
Token(): count(OUTSIDE) {}
|
||||
|
||||
///the new priority will be effective only after a call to Controller::updatePriorities()
|
||||
void setPriority(const Priority &p) {
|
||||
new_priority = p;
|
||||
}
|
||||
Priority getPriority() {
|
||||
return new_priority;
|
||||
}
|
||||
///return false if resource not in highest query. remember to unlock when done
|
||||
bool lock() {
|
||||
if(count.fetchAndAddAcquire(1) >= 0) return true;
|
||||
count.deref();
|
||||
return false;
|
||||
}
|
||||
///assumes it was locked first and 1 unlock for each lock.
|
||||
bool unlock() {
|
||||
return count.deref();
|
||||
}
|
||||
|
||||
///can't be removed if locked and will return false
|
||||
bool remove() {
|
||||
count.testAndSetOrdered(READY, REMOVE);
|
||||
count.testAndSetOrdered(CACHE, REMOVE);
|
||||
return count <= REMOVE; //might have become OUSIDE in the meanwhile
|
||||
}
|
||||
|
||||
bool isLocked() { return count > 0; }
|
||||
bool isInCache() { return count != OUTSIDE; } //careful, can be used only when provider thread is locked.
|
||||
|
||||
///copy priority to swap space [do not use, should be private]
|
||||
void pushPriority() {
|
||||
tmp_priority = new_priority;
|
||||
}
|
||||
///copy priority from swap space [do not use, should be private]
|
||||
void pullPriority() {
|
||||
priority = tmp_priority;
|
||||
}
|
||||
|
||||
bool operator<(const Token &a) const {
|
||||
if(count == a.count)
|
||||
return priority < a.priority;
|
||||
return count < a.count;
|
||||
}
|
||||
bool operator>(const Token &a) const {
|
||||
if(count == a.count)
|
||||
return priority > a.priority;
|
||||
return count > a.count;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GCACHE_H
|
Loading…
Reference in New Issue