+
+typedef struct WorkPlain {
+ SFSTree *info;
+ char *node;
+ off_t offset;
+} WorkPlain;
+
+static Opaque
+plainNode(WorkPlain *wp, SFSNode *node) {
+ int size = getNodeSize(wp->info, node);
+ off_t myoffset = wp->offset;
+
+ memcpy( wp->node + wp->offset, node, size );
+ wp->offset += size;
+
+ tassert( wp->offset <= wp->info->totalen );
+
+ if ( node->isskip ) {
+ if (node->haschild)
+ *(Opaque*)(wp->node + myoffset + SFSNHRDSZ) =
+ plainNode(wp, getSkipChildPointer(wp->info, node));
+ } else {
+ SFSNodeData *nd = node->data;
+ u_int32_t i;
+
+ for(i=0;i<node->nchar;i++) {
+ if (nd->haschild)
+ *(Opaque*)(wp->node + myoffset + ( ((char*)nd) - ((char*)node) ) + nd->child) =
+ plainNode(wp, getChildPointer( wp->info, nd ) );
+ nd++;
+ }
+ }
+
+ tfree(node);
+
+ return myoffset;
+}
+
+void
+SFSMakePlain(SFSTree *info, void *pointer) {
+ WorkPlain wp;
+
+ if ( info->plainmemory )
+ return;
+
+ wp.info = info;
+ wp.offset = 0;
+ if ( pointer )
+ wp.node = (char*)pointer;
+ else
+ wp.node = (char*)tmalloc(sizeof(char*) * info->totalen);
+
+ plainNode(&wp, info->node);
+ tassert( wp.offset == info->totalen );
+
+ info->node = (SFSNode*)wp.node;
+ info->plainmemory = 1;
+}
+
+static SFSNode*
+revertNode(SFSTree *info, SFSNode* node) {
+ int size = getNodeSize(info, node);
+ SFSNode *newnode;
+
+ newnode = (SFSNode*)tmalloc( size );
+ memcpy(newnode, node, size);
+
+ if ( node->isskip ) {
+ if (node->haschild)
+ *(SFSNode**)( (char*)(newnode->data) ) =
+ revertNode(info, getSkipChildPointer(info, node));
+ } else {
+ SFSNodeData *nd = node->data;
+ SFSNodeData *nnd = newnode->data;
+ u_int32_t i;
+
+ for(i=0;i<node->nchar;i++) {
+ if (nd->haschild)
+ *(SFSNode**) (((char*)nnd) + nnd->child ) =
+ revertNode(info, getChildPointer( info, nd ));
+ nd++; nnd++;
+ }
+ }
+
+ return newnode;
+}
+
+void *
+SFSRevertPlain(SFSTree *info) {
+ void *pointer = info->node;
+
+ if (! info->plainmemory )
+ return NULL;
+
+ info->node = revertNode(info, info->node);
+ info->plainmemory = 0;
+
+ return pointer;
+}
+
+static Opaque
+writeNode(WorkPlain *wp, int fd, SFSNode *node) {
+ int size = getNodeSize(wp->info, node);
+ SFSNode *wnode = (SFSNode*)tmalloc(size);
+ off_t myoffset = wp->offset;
+
+ memcpy( wnode, node, size );
+ wp->offset += size;
+
+ tassert( wp->offset <= wp->info->totalen );
+
+ if ( node->isskip ) {
+ if (node->haschild)
+ *(Opaque*)( ((char*)wnode) + SFSNHRDSZ) =
+ writeNode(wp, fd, getSkipChildPointer(wp->info, node));
+ } else {
+ SFSNodeData *nd = node->data;
+ u_int32_t i;
+
+ for(i=0;i<node->nchar;i++) {
+ if (nd->haschild)
+ *(Opaque*)(((char*)wnode) + ( ((char*)nd) - ((char*)node) ) + nd->child) =
+ writeNode(wp, fd, getChildPointer( wp->info, nd ) );
+ nd++;
+ }
+ }
+
+ if ( lseek(fd, myoffset + SFSTDHSZ, SEEK_SET) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "lseek failed: %s", strerror(errno));
+ if ( write(fd, wnode, size) != size )
+ tlog(TL_CRIT|TL_EXIT, "write failed: %s", strerror(errno));
+
+ tfree(wnode);
+
+ return myoffset;
+}
+
+void
+SFSWriteDump(SFSTree *info, char *filename) {
+ int fd;
+ off_t size = info->totalen + SFSTDHSZ;
+ SFSTreeDumpHeader dh;
+
+ if ( (fd = open(filename, O_RDWR|O_CREAT|O_TRUNC|O_EXLOCK, 0666)) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "Can not open file '%s': %s", strerror(errno));
+
+ if ( lseek(fd, size, SEEK_SET) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "lseek failed: %s", strerror(errno));
+
+ dh.version = SFSTREE_VERSION;
+ dh.opaquesize = sizeof(Opaque);
+ dh.headersize = SFSTDHSZ;
+ dh.datasize = info->datasize;
+ dh.totalen = info->totalen;
+ dh.nnodes = info->nnodes;
+
+ if ( lseek(fd, 0, SEEK_SET) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "lseek failed: %s", strerror(errno));
+ if ( write(fd, &dh, SFSTDHSZ) != SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "write failed: %s", strerror(errno));
+
+ if ( info->plainmemory ) {
+ if ( write(fd, info->node, info->totalen) != info->totalen )
+ tlog(TL_CRIT|TL_EXIT, "write failed: %s", strerror(errno));
+ } else {
+ WorkPlain wp;
+
+ wp.info = info;
+ wp.offset = 0;
+
+ writeNode(&wp, fd, info->node);
+ }
+
+ close(fd);
+}
+
+static SFSNode*
+readNode(SFSTree *info, int fd, char *buf, int bufsize) {
+ SFSNode *node;
+ int size;
+
+ size = read(fd, buf, bufsize );
+ if ( size < SFSNHRDSZ + sizeof(void*) )
+ tlog(TL_CRIT|TL_EXIT, "read failed: %s", strerror(errno));
+
+ size = getNodeSize(info, (SFSNode*)buf);
+ tassert( size <= bufsize );
+ node = (SFSNode*)tmalloc( size );
+ memcpy(node, buf, size);
+
+ if ( node->isskip ) {
+ if (node->haschild) {
+ if ( lseek(fd, *(Opaque*)node->data + SFSTDHSZ, SEEK_SET) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "lseek failed: %s", strerror(errno));
+ *(SFSNode**)( (char*)(node->data) ) =
+ readNode(info, fd, buf, bufsize);
+ }
+ } else {
+ SFSNodeData *nd = node->data;
+ u_int32_t i;
+
+ for(i=0;i<node->nchar;i++) {
+ if (nd->haschild) {
+ if ( lseek(fd, *(Opaque*)(((char*)nd) + nd->child ) + SFSTDHSZ, SEEK_SET) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "lseek failed: %s", strerror(errno));
+ *(SFSNode**) (((char*)nd) + nd->child ) =
+ readNode(info, fd, buf, bufsize);
+ }
+ nd++;
+ }
+ }
+
+ return node;
+}
+
+void
+SFSReadDump(SFSTree *info, char *filename) {
+ int fd;
+ SFSTreeDumpHeader dh;
+ char *buf;
+ int bufsize;
+
+ memset(info,0,sizeof(SFSTree));
+
+ if ( (fd = open(filename, O_RDONLY|O_SHLOCK)) < 0 )
+ tlog(TL_CRIT|TL_EXIT, "Can not open file '%s': %s", strerror(errno));
+
+ if ( read(fd, &dh, SFSTDHSZ) != SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "read failed: %s", strerror(errno));
+
+ if ( dh.version != SFSTREE_VERSION )
+ tlog(TL_CRIT|TL_EXIT, "Tree version mismatch (should be 0x%04x but 0x%04x)", SFSTREE_VERSION, dh.version);
+ if ( dh.opaquesize != sizeof(Opaque) )
+ tlog(TL_CRIT|TL_EXIT, "sizeof(Opaque) mismatch");
+ if ( dh.headersize != SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "Tree's header size mismatch (should be %d but %d bytes)", SFSTDHSZ, dh.headersize);
+
+ info->totalen = dh.totalen;
+ info->nnodes = dh.nnodes;
+ info->datasize = dh.datasize;
+
+ /* allocate buffer with max allowed size */
+ bufsize = SFSNHRDSZ + 256*(sizeof(SFSNodeData) + sizeof(void*) + info->datasize);
+ buf = tmalloc( bufsize );
+ info->node = readNode(info, fd, buf, bufsize);
+ tfree(buf);
+
+ close(fd);
+}
+
+void
+SFSInitFromDump(SFSTree *info, void *pointer, u_int64_t size) {
+ SFSTreeDumpHeader *dh;
+
+ memset(info,0,sizeof(SFSTree));
+
+ if ( size && size < SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "Memsize too small");
+
+ dh = (SFSTreeDumpHeader*)pointer;
+
+ if ( dh->version != SFSTREE_VERSION )
+ tlog(TL_CRIT|TL_EXIT, "Tree version mismatch (should be 0x%04x but 0x%04x)", SFSTREE_VERSION, dh->version);
+ if ( dh->opaquesize != sizeof(Opaque) )
+ tlog(TL_CRIT|TL_EXIT, "sizeof(Opaque) mismatch");
+ if ( dh->headersize != SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "Tree's header size mismatch (should be %d but %d bytes)", SFSTDHSZ, dh->headersize);
+ if ( size && size != dh->totalen + SFSTDHSZ )
+ tlog(TL_CRIT|TL_EXIT, "Memory size mismatch (should be %d but %d bytes)", dh->totalen + SFSTDHSZ, size);
+
+ info->totalen = dh->totalen;
+ info->nnodes = dh->nnodes;
+ info->datasize = dh->datasize;
+ info->plainmemory = 1;
+
+ if ( info->totalen && info->nnodes )
+ info->node = (SFSNode*)( ((char*)pointer) + SFSTDHSZ );
+}