Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
pike
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pikelang
pike
Commits
cc8bdba3
Commit
cc8bdba3
authored
May 23, 1998
by
Fredrik Noring
Browse files
Options
Downloads
Patches
Plain Diff
The Yabu database is now included in Pike.
Rev: lib/modules/Yabu.pmod/module.pmod:1.1
parent
12887f8f
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
.gitattributes
+1
-0
1 addition, 0 deletions
.gitattributes
lib/modules/Yabu.pmod/module.pmod
+943
-0
943 additions, 0 deletions
lib/modules/Yabu.pmod/module.pmod
with
944 additions
and
0 deletions
.gitattributes
+
1
−
0
View file @
cc8bdba3
...
@@ -70,6 +70,7 @@ testfont binary
...
@@ -70,6 +70,7 @@ testfont binary
/lib/modules/Sql.pmod/sql.pike foreign_ident
/lib/modules/Sql.pmod/sql.pike foreign_ident
/lib/modules/Sql.pmod/sql_result.pike foreign_ident
/lib/modules/Sql.pmod/sql_result.pike foreign_ident
/lib/modules/Standards.pmod/ASN1.pmod/decode.pike foreign_ident
/lib/modules/Standards.pmod/ASN1.pmod/decode.pike foreign_ident
/lib/modules/Yabu.pmod/module.pmod foreign_ident
/lib/modules/error.pmod foreign_ident
/lib/modules/error.pmod foreign_ident
/man/hilfe.1 foreign_ident
/man/hilfe.1 foreign_ident
/man/pike.1 foreign_ident
/man/pike.1 foreign_ident
...
...
This diff is collapsed.
Click to expand it.
lib/modules/Yabu.pmod/module.pmod
0 → 100644
+
943
−
0
View file @
cc8bdba3
// Yabu by Fredrik Noring
// $Id: module.pmod,v 1.1 1998/05/23 19:51:26 noring Exp $
#if constant(thread_create)
#define THREAD_SAFE
#define LOCK() do { object key; catch(key=lock())
#define UNLOCK() key=0; } while(0)
#else
#undef THREAD_SAFE
#define LOCK() do {
#define UNLOCK() } while(0)
#endif
#define ERR(msg) throw(({ "(Yabu) "+msg+"\n", backtrace() }))
#define WARN(msg) werror(msg)
#define DEB(msg) /* werror(msg) */
#define CHECKSUM(s) (hash(s) & 0xffffffff)
static private class FileIO {
static private inherit Stdio.File:file;
static private void seek(int offset)
{
if(file::seek(offset) == -1)
ERR("seek failed");
}
string read_at(int offset, int|void size)
{
seek(offset);
mixed s = size?file::read(size):file::read();
if(!stringp(s))
ERR("read failed");
return s;
}
void write_at(int offset, string s)
{
seek(offset);
while(sizeof(s)) {
int n = file::write(s);
if(n < 0 && !(<11,12,16,24,28,49>)[file::errno()])
ERR(strerror(file::errno()));
if(n == sizeof(s))
break;
s = s[n..];
if(n<0)
WARN(strerror(file::errno())+" (sleeping)");
else
WARN("disk seems to be full. (sleeping)");
sleep(1);
}
}
void create(string filename, string mode)
{
file::create();
if(!file::open(filename, mode))
ERR(strerror(file::errno()));
}
}
class Chunk {
static private inherit FileIO:file;
static private object parent;
static private int magic, compress, write;
static private string start_time, filename;
static private int eof = 0;
static private mapping frees = ([]), keys = ([]);
static private string escape(string s)
{
return replace(s, ({ "\n", "%" }), ({ "%n", "%p" }));
}
static private string descape(string s)
{
return replace(s, ({ "%n", "%p" }), ({ "\n", "%" }));
}
static private string encode_num(int x)
{
return sprintf("%08x", x);
}
static private int decode_num(string s, int offset)
{
int x;
if(sizeof(s) < offset+8)
ERR("cunk short");
if(sscanf(s[offset..offset+7], "%x", x) != 1)
ERR("cunk number decode error");
return x;
}
static private string encode_key(int offset, int type)
{
return encode_num(offset)+":"+encode_num(type);
}
#define DECODE_KEY(key, offset, type) \
{ if(sizeof(key) != 17 || sscanf(key, "%x:%x", offset, type) != 2) \
ERR("Key not valid"); }
static private string next_magic()
{
return start_time + encode_num(magic++);
}
static private int find_nearest_2x(int num)
{
for(int b=4;b<32;b++) if((1<<b) >= num) return (1<<b);
}
static private mapping null_chunk(int t)
{
string entry = "";
string magic = next_magic();
string type = encode_num(t);
string checksum = encode_num(CHECKSUM(entry));
string size = encode_num(sizeof(entry));
return ([ "type":t,
"entry":("\n"+magic+type+checksum+size+entry+"%m"+magic)]);
}
static private mapping encode_chunk(mixed x)
{
string entry = encode_value(x);
#if constant(Gz.inflate)
if(compress)
catch { entry = Gz.deflate()->deflate(entry); };
#endif
string entry = escape(entry);
string magic = next_magic();
string checksum = encode_num(CHECKSUM(entry));
string size = encode_num(sizeof(entry));
entry = checksum+size+entry+"%m"+magic;
int t = find_nearest_2x(1+16+8+sizeof(entry));
string type = encode_num(t);
entry = "\n"+magic+type+entry;
return ([ "type":t, "entry":entry ]);
}
static private mapping decode_chunk(string chunk)
{
mapping m = ([]);
if(chunk[0] != '\n')
ERR("No chunk start");
chunk = chunk[1..];
m->magic = chunk[0..15];
m->type = decode_num(chunk, 16);
m->checksum = decode_num(chunk, 24);
m->size = decode_num(chunk, 32);
chunk = chunk[40..];
m->entry = chunk[0..m->size-1];
if(sizeof(m->entry) < m->size)
ERR("Chunk too small");
if(CHECKSUM(m->entry) != m->checksum)
ERR("Chunk checksum error");
if(chunk[m->size..m->size+1] != "%m")
ERR("No magic");
if(m->magic != chunk[m->size+2..m->size+17])
ERR("Magic diff");
if(m->size) {
m->entry = descape(m->entry);
#if constant(Gz.inflate)
catch { m->entry = Gz.inflate()->inflate(m->entry); };
#endif
m->entry = decode_value(m->entry);
}
return m;
}
static private int allocate_chunk(int type, mapping m)
{
array f;
if(f = frees[type]) {
if(sizeof(f) > 1)
frees[type] = f[1..];
else
m_delete(frees, type);
return f[0];
}
int x = eof;
eof += type;
return x;
}
array(string) list_keys()
{
return indices(keys);
}
string set(mixed x)
{
mapping m = encode_chunk(x);
int offset = allocate_chunk(m->type, m);
string key = encode_key(offset, m->type);
file::write_at(offset, m->entry);
keys[key] = offset;
return key;
}
mixed get(string key, void|int attributes)
{
if(!attributes && zero_type(keys[key]))
ERR(sprintf("Unknown key '%O'", key));
int offset, type;
DECODE_KEY(key, offset, type);
mapping m = decode_chunk(file::read_at(offset, type));
if(m->size == 0)
ERR("Cannot decode free chunk!");
if(attributes)
return m;
return m->entry;
}
void free(string key)
{
if(zero_type(keys[key]))
ERR(sprintf("Unknown key '%O'", key));
int offset, type;
DECODE_KEY(key, offset, type);
m_delete(keys, key);
file::write_at(offset, null_chunk(type)->entry);
frees[type] = (frees[type]||({})) | ({ offset });
}
mapping state(array|void almost_free)
{
mapping m = copy_value(frees);
foreach(almost_free||({}), string key) {
int offset, type;
DECODE_KEY(key, offset, type);
m[type] = (m[type]||({})) | ({ offset });
}
return copy_value(([ "eof":eof, "keys":keys, "frees":m ]));
}
void purge()
{
if(!write)
ERR("Cannot purge in read mode");
rm(filename);
destruct(this_object());
}
void destroy()
{
if(parent)
destruct(parent);
}
void create(string filename_in, string mode,
object|void parent_in, mapping|void m)
{
magic = 0;
filename = filename_in;
parent = parent_in;
start_time = encode_num(time());
if(search(mode, "C")+1)
compress = 1;
if(search(mode, "w")+1) {
write = 1;
file::create(filename, "cwr");
} else
file::create(filename, "r");
if(m) {
eof = m->eof;
keys = m->keys||([]);
frees = m->frees||([]);
} else {
string s;
array offsets = ({});
int n, offset = 0;
while(sizeof(s = file::read_at(offset, 4096))) {
n = -1;
while((n = search(s, "\n", n+1)) >= 0)
offsets += ({ offset+n });
offset += 4096;
}
eof = 0;
for(int i = 0; i < sizeof(offsets); i++) {
int offset = offsets[i];
int size = (i+1 < sizeof(offsets)?offsets[i+1]-offset:0);
string key = encode_key(offset, size);
if(!size || size == find_nearest_2x(size)) {
mapping m;
if(catch(m = get(key, 1))) {
if(size) {
frees[size] = (frees[size]||({})) + ({ offset });
eof = offset+size;
}
} else {
keys[encode_key(offset, m->type)] = offset;
eof = offset+m->type;
}
}
}
}
}
}
class Transaction {
static private int id;
static private object table, keep_ref;
void sync()
{
table->sync();
}
void destroy()
{
if(table)
table->t_destroy(id);
}
mixed set(string handle, mixed x)
{
return table->t_set(id, handle, x);
}
mixed get(string handle)
{
return table->t_get(id, handle);
}
mixed delete(string handle)
{
return table->t_delete(id, handle);
}
array list_keys()
{
return table->t_list_keys(id);
}
void commit()
{
table->t_commit(id);
}
void rollback()
{
table->t_rollback(id);
}
mixed `[]=(string handle, mixed x)
{
return set(handle, x);
}
mixed `[](string handle)
{
return get(handle);
}
array _indices()
{
return list_keys();
}
void create(object table_in, int id_in, object keep_ref_in)
{
table = table_in;
id = id_in;
keep_ref = keep_ref_in;
}
}
class Table {
#ifdef THREAD_SAFE
static inherit Thread.Mutex;
#endif
static private object index, db;
static private string filename;
static private mapping handles, new, changes;
static private mapping t_start, t_changes, t_handles, t_deleted;
static private int sync_timeout, write, dirty, magic, id = 0x314159;
void sync()
{
if(!write || !dirty) return;
LOCK();
new = ([]);
array almost_free = db->list_keys() - values(handles);
string key = index->set(([ "db":db->state(almost_free),
"handles":handles ]));
foreach(index->list_keys() - ({ key }), string k)
index->free(k);
array working = ({});
if(t_handles)
foreach(indices(t_handles), int id)
working += values(t_handles[id]);
foreach(almost_free - working, string k)
db->free(k);
dirty = sizeof(working);
UNLOCK();
}
static private int next_magic()
{
return magic++;
}
static private void modified()
{
dirty++;
if(sync_timeout && dirty >= sync_timeout)
sync();
}
static private mixed _set(string handle, mixed x,
mapping handles, mapping new)
{
if(!write) ERR("Cannot set in read mode");
string key = handles[handle];
if(key && new[key]) {
m_delete(new, key);
db->free(key);
}
handles[handle] = key = db->set(([ "handle":handle, "entry":x ]));
new[key] = 1;
modified();
return x;
}
static private mixed _get(string handle, mapping handles)
{
if(!handles[handle])
return 0;
return db->get(handles[handle])->entry;
}
void delete(string handle)
{
if(!write) ERR("Cannot delete in read mode");
LOCK();
if(!handles[handle]) ERR(sprintf("Unknown handle '%O'", handle));
string key = handles[handle];
m_delete(handles, handle);
if(changes)
changes[handle] = next_magic();
// m_delete(changes, handle);
if(new[key]) {
m_delete(new, key);
db->free(key);
}
modified();
UNLOCK();
}
mixed set(string handle, mixed x)
{
LOCK();
if(changes)
changes[handle] = next_magic();
return _set(handle, x, handles, new);
UNLOCK();
}
mixed get(string handle)
{
LOCK();
return _get(handle, handles);
UNLOCK();
}
//
// Transactions
//
mixed t_set(int id, string handle, mixed x)
{
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
t_changes[id][handle] = t_start[id];
mapping t_new = mkmapping(values(t_handles[id]), indices(t_handles[id]));
return _set(handle, x, t_handles[id], t_new);
UNLOCK();
}
mixed t_get(int id, string handle)
{
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
t_changes[id][handle] = t_start[id];
if(t_deleted[id][handle])
return 0;
return _get(handle, t_handles[id][handle]?t_handles[id]:handles);
UNLOCK();
}
void t_delete(int id, string handle)
{
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
t_deleted[id][handle] = 1;
t_changes[id][handle] = t_start[id];
if(t_handles[id][handle]) {
db->free(t_handles[id][handle]);
m_delete(t_handles[id], handle);
}
UNLOCK();
}
void t_commit(int id)
{
if(!write) ERR("Cannot commit in read mode");
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
foreach(indices(t_changes[id]), string handle)
if(t_changes[id][handle] < changes[handle])
ERR("Transaction conflict");
mapping tmp_t_changes = copy_value(t_changes);
mapping tmp_t_handles = copy_value(t_handles);
t_start[id] = next_magic();
t_changes[id] = ([]);
t_handles[id] = ([]);
t_deleted[id] = ([]);
array obsolete = ({});
foreach(indices(tmp_t_changes[id]), string handle) {
if(new[handles[handle]])
obsolete += ({ handles[handle] });
changes[handle] = next_magic();
handles[handle] = tmp_t_handles[id][handle];
}
foreach(obsolete, string key) {
m_delete(new, key);
db->free(key);
}
UNLOCK();
}
void t_rollback(int id)
{
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
array keys = values(t_handles[id]);
t_start[id] = next_magic();
t_changes[id] = ([]);
t_handles[id] = ([]);
t_deleted[id] = ([]);
foreach(keys, string key)
db->free(key);
UNLOCK();
}
array t_list_keys(int id)
{
if(!t_handles[id]) ERR("Unknown transaction id");
LOCK();
return Array.uniq(indices(handles) + indices(t_handles[id]));
UNLOCK();
}
void t_destroy(int id)
{
if(!t_handles[id]) ERR("Unknown transaction id");
t_rollback(id);
LOCK();
m_delete(t_start, id);
m_delete(t_changes, id);
m_delete(t_handles, id);
m_delete(t_deleted, id);
UNLOCK();
}
object transaction(object|void keep_ref)
{
if(!changes) ERR("Transactions are not enabled");
LOCK();
id++;
t_start[id] = next_magic();
t_changes[id] = ([]);
t_handles[id] = ([]);
t_deleted[id] = ([]);
UNLOCK();
return Transaction(this_object(), id, keep_ref);
}
//
//
//
array list_keys()
{
LOCK();
return indices(handles);
UNLOCK();
}
//
// Interface functions
//
void sync_schedule()
{
sync();
call_out(sync_schedule, 120);
}
mixed `[]=(string handle, mixed x)
{
return set(handle, x);
}
mixed `[](string handle)
{
return get(handle);
}
void destroy()
{
sync();
}
void _destroy()
{
write = 0;
destruct(this_object());
}
void close()
{
sync();
_destroy();
}
void purge()
{
if(!write) ERR("Cannot purge in read mode");
LOCK();
write = 0;
rm(filename+".inx");
rm(filename+".chk");
destruct(this_object());
UNLOCK();
}
array _indices()
{
return list_keys();
}
void create(string filename_in, string mode)
{
filename = filename_in;
if(search(mode, "w")+1)
write = 1;
if(search(mode, "t")+1) {
changes = ([]);
t_start = ([]);
t_changes = ([]);
t_handles = ([]);
t_deleted = ([]);
}
index = Chunk(filename+".inx", mode, this_object());
mapping m = ([]);
if(sizeof(index->list_keys()))
m = index->get(sort(index->list_keys())[-1]);
handles = m->handles||([]);
new = ([]);
db = Chunk(filename+".chk", mode, this_object(), m->db||([]));
if(write && search(mode, "s")+1) {
sync_timeout = 32;
sync_schedule();
}
}
}
class _Table {
static string handle;
static object table, parent;
void sync() { table->sync(); }
void delete(string handle) { table->delete(handle); }
mixed set(string handle, mixed x) { return table->set(handle, x); }
mixed get(string handle) { return table->get(handle); }
array list_keys() { return table->list_keys(); }
object transaction() { return table->transaction(this_object()); }
void close() { destruct(this_object()); }
void purge() { table->purge(); }
mixed `[]=(string handle, mixed x)
{
return set(handle, x);
}
mixed `[](string handle)
{
return get(handle);
}
array _indices()
{
return list_keys();
}
void destroy()
{
if(parent)
parent->_table_destroyed(handle);
}
void create(string _handle, object _table, object _parent)
{
handle = _handle;
table = _table;
parent = _parent;
}
}
class db {
#ifdef THREAD_SAFE
static inherit Thread.Mutex;
#endif
static string dir, mode;
static mapping tables = ([]), table_refs = ([]);
static int write, id;
void sync()
{
LOCK();
foreach(values(tables), object o)
if(o)
o->sync();
UNLOCK();
}
object table(string handle)
{
LOCK();
if(!tables[handle])
tables[handle] = Table(dir+"/"+handle, mode);
table_refs[handle]++;
// DEB(sprintf("### refs[%s]++ = %d\n", handle, table_refs[handle]));
DEB(sprintf("# tables '%s': %d (%s)\n",
reverse(reverse(dir/"/")[0..1])*"/", sizeof(tables), handle));
return _Table(handle, tables[handle], this_object());
UNLOCK();
}
void _table_destroyed(string handle)
{
LOCK();
table_refs[handle]--;
// DEB(sprintf("### refs[%s]-- = %d\n", handle, table_refs[handle]));
DEB(sprintf("! tables '%s': %d (%s)\n",
reverse(reverse(dir/"/")[0..1])*"/", sizeof(tables), handle));
if(!table_refs[handle]) {
// DEB(sprintf("### zonking[%s]\n", handle));
destruct(tables[handle]);
m_delete(tables, handle);
m_delete(table_refs, handle);
}
UNLOCK();
}
mixed `[](string handle)
{
return table(handle);
}
array list_tables()
{
LOCK();
return Array.map(glob("*.chk", get_dir(dir)||({})),
lambda(string s) { return s[0..sizeof(s)-5]; });
UNLOCK();
}
array _indices()
{
return list_tables();
}
// Remove maximum one level of directories and files
static private void level2_rm(string f)
{
if(sizeof(f) > 1 && f[-1] == '/')
f = f[0..sizeof(f)-2]; // remove /'s
if((file_stat(f)||({0,0}))[1] == -2) // directory
foreach(get_dir(f)||({}), string file)
rm(f+"/"+file); // delete file
rm(f); // delete file/directory
}
void purge()
{
LOCK();
foreach(values(tables), object o)
if(o)
destruct(o);
level2_rm(dir);
destruct(this_object());
UNLOCK();
}
static void mkdirhier(string from)
{
string a, b;
array f;
f=(from/"/");
b="";
foreach(f[0..sizeof(f)-2], a)
{
mkdir(b+a);
b+=a+"/";
}
}
void create(string dir_in, string mode_in)
{
dir = dir_in;
mode = mode_in;
if(search(mode, "w")+1) {
write = 1;
mkdirhier(dir+"/");
}
}
}
class LookupTable {
#ifdef THREAD_SAFE
static inherit Thread.Mutex;
#endif
static private int minx;
static private object table;
static private string h(string s)
{
return (string)(hash(s) & minx);
}
mixed get(string handle)
{
LOCK();
return (table->get(h(handle))||([]))[handle];
UNLOCK();
}
mixed set(string handle, mixed x)
{
LOCK();
mapping m = table->get(h(handle))||([]);
m[handle] = x;
return table->set(h(handle), m)[handle];
UNLOCK();
}
mixed `[]=(string handle, mixed x)
{
return set(handle, x);
}
mixed `[](string handle)
{
return get(handle);
}
void purge()
{
LOCK();
table->purge();
UNLOCK();
}
void sync()
{
LOCK();
table->sync();
UNLOCK();
}
void create(string filename, string mode, int _minx)
{
minx = _minx;
table = Table(filename, mode);
}
}
class lookup {
inherit db;
static private int minx;
object table(string handle)
{
LOCK();
return (tables[handle] =
tables[handle]||LookupTable(dir+"/"+handle, mode, minx));
UNLOCK();
}
void create(string dir, string mode, mapping|void opt)
{
::create(dir, (mode-"t"));
minx = (opt||([]))->index_size || 0x7ff;
if(!sscanf(Stdio.read_bytes(dir+"/hs")||"", "%4c", minx))
Stdio.write_file(dir+"/hs", sprintf("%4c", minx));
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment