/** Compare two encoded values * a, b - encoded values * returns WG_GREATER, WG_EQUAL or WG_LESSTHAN * assumes that a and b themselves are not equal and so * their decoded values need to be examined (which could still * be equal for some data types). * depth - recursion depth for records */ gint wg_compare(void *db, gint a, gint b, int depth) { /* a very simplistic version of the function: * - we get the types of the variables * - if the types match, compare the decoded values * - otherwise compare the type codes (not really scientific, * but will provide a means of ordering values). * * One important point that should be observed here is * that the returned values should be consistent when * comparing A to B and then B to A. This applies to cases * where we have no reason to think one is greater than * the other from the *user's* point of view, but for use * in T-tree index and similar, values need to be consistently * ordered. Examples include unknown types and record pointers * (once recursion depth runs out). */ /* XXX: might be able to save time here to mask and compare * the type bits instead */ gint typea = wg_get_encoded_type(db, a); gint typeb = wg_get_encoded_type(db, b); /* assume types are >2 (NULLs are always equal) and * <13 (not implemented as of now) * XXX: all of this will fall apart if type codes * are somehow rearranged :-) */ if(typeb==typea) { if(typea>WG_CHARTYPE) { /* > 9, not a string */ if(typea>WG_FIXPOINTTYPE) { /* date or time. Compare decoded gints */ gint deca, decb; if(typea==WG_DATETYPE) { deca = wg_decode_date(db, a); decb = wg_decode_date(db, b); } else if(typea==WG_TIMETYPE) { deca = wg_decode_time(db, a); decb = wg_decode_time(db, b); } else if(typea==WG_VARTYPE) { deca = wg_decode_var(db, a); decb = wg_decode_var(db, b); } else { /* anon const or other new type, no idea how to compare */ return (a>b ? WG_GREATER : WG_LESSTHAN); } return (deca>decb ? WG_GREATER : WG_LESSTHAN); } else { /* fixpoint, need to compare doubles */ double deca, decb; deca = wg_decode_fixpoint(db, a); decb = wg_decode_fixpoint(db, b); return (deca>decb ? WG_GREATER : WG_LESSTHAN); } } else if(typea<WG_STRTYPE) { /* < 5, still not a string */ if(typea==WG_RECORDTYPE) { void *deca, *decb; deca = wg_decode_record(db, a); decb = wg_decode_record(db, b); if(!depth) { /* No more recursion allowed and pointers aren't equal. * So while we're technically comparing the addresses here, * the main point is that the returned value != WG_EQUAL */ return ((gint) deca> (gint) decb ? WG_GREATER : WG_LESSTHAN); } else { int i; #ifdef USE_CHILD_DB void *parenta, *parentb; #endif int lena = wg_get_record_len(db, deca); int lenb = wg_get_record_len(db, decb); #ifdef USE_CHILD_DB /* Determine, if the records are inside the memory area beloning * to our current base address. If it is outside, the encoded * values inside the record contain offsets in relation to * a different base address and need to be translated. */ parenta = wg_get_rec_owner(db, deca); parentb = wg_get_rec_owner(db, decb); #endif /* XXX: Currently we're considering records of differing lengths * non-equal without comparing the elements */ if(lena!=lenb) return (lena>lenb ? WG_GREATER : WG_LESSTHAN); /* Recursively check each element in the record. If they * are not equal, break and return with the obtained value */ for(i=0; i<lena; i++) { gint elema = wg_get_field(db, deca, i); gint elemb = wg_get_field(db, decb, i); #ifdef USE_CHILD_DB if(parenta != dbmemseg(db)) { elema = wg_translate_hdroffset(db, parenta, elema); } if(parentb != dbmemseg(db)) { elemb = wg_translate_hdroffset(db, parentb, elemb); } #endif if(elema != elemb) { gint cr = wg_compare(db, elema, elemb, depth - 1); if(cr != WG_EQUAL) return cr; } } return WG_EQUAL; /* all elements matched */ } } else if(typea==WG_INTTYPE) { gint deca, decb; deca = wg_decode_int(db, a); decb = wg_decode_int(db, b); if(deca==decb) return WG_EQUAL; /* large ints can be equal */ return (deca>decb ? WG_GREATER : WG_LESSTHAN); } else { /* WG_DOUBLETYPE */ double deca, decb; deca = wg_decode_double(db, a); decb = wg_decode_double(db, b); if(deca==decb) return WG_EQUAL; /* decoded doubles can be equal */ return (deca>decb ? WG_GREATER : WG_LESSTHAN); } } else { /* string */ /* Need to compare the characters. In case of 0-terminated * strings we use strcmp() directly, which in glibc is heavily * optimised. In case of blob type we need to query the length * and use memcmp(). */ char *deca, *decb, *exa=NULL, *exb=NULL; char buf[4]; gint res; if(typea==WG_STRTYPE) { /* lang is ignored */ deca = wg_decode_str(db, a); decb = wg_decode_str(db, b); } else if(typea==WG_URITYPE) { exa = wg_decode_uri_prefix(db, a); exb = wg_decode_uri_prefix(db, b); deca = wg_decode_uri(db, a); decb = wg_decode_uri(db, b); } else if(typea==WG_XMLLITERALTYPE) { exa = wg_decode_xmlliteral_xsdtype(db, a); exb = wg_decode_xmlliteral_xsdtype(db, b); deca = wg_decode_xmlliteral(db, a); decb = wg_decode_xmlliteral(db, b); } else if(typea==WG_CHARTYPE) { buf[0] = wg_decode_char(db, a); buf[1] = '\0'; buf[2] = wg_decode_char(db, b); buf[3] = '\0'; deca = buf; decb = &buf[2]; } else { /* WG_BLOBTYPE */ deca = wg_decode_blob(db, a); decb = wg_decode_blob(db, b); } if(exa || exb) { /* String type where extra information is significant * (we're ignoring this for plain strings and blobs). * If extra part is equal, normal comparison continues. If * one string is missing altogether, it is considered to be * smaller than the other string. */ if(!exb) { if(exa[0]) return WG_GREATER; } else if(!exa) { if(exb[0]) return WG_LESSTHAN; } else { res = strcmp(exa, exb); if(res > 0) return WG_GREATER; else if(res < 0) return WG_LESSTHAN; } } #if 0 /* paranoia check */ if(!deca || !decb) { if(decb) if(decb[0]) return WG_LESSTHAN; } else if(deca) { if(deca[0]) return WG_GREATER; } return WG_EQUAL; } #endif if(typea==WG_BLOBTYPE) { /* Blobs are not 0-terminated */ int lena = wg_decode_blob_len(db, a); int lenb = wg_decode_blob_len(db, b); res = memcmp(deca, decb, (lena < lenb ? lena : lenb)); if(!res) res = lena - lenb; } else { res = strcmp(deca, decb); } if(res > 0) return WG_GREATER; else if(res < 0) return WG_LESSTHAN; else return WG_EQUAL; } }
char* sprint_value(void *db, wg_int enc, char **buf, int *bufsize, char **bptr, int format, int showid, int depth, int maxdepth, int strenc) { wg_int *ptrdata; int intdata,strl,strl1,strl2; char *strdata, *exdata; double doubledata; char strbuf[80]; // tmp area for dates int limit=MIN_STRLEN; switch(wg_get_encoded_type(db, enc)) { case WG_NULLTYPE: str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); if (format!=0) { // json snprintf(*bptr, limit, JS_NULL); return *bptr+strlen(*bptr); } return *bptr; case WG_RECORDTYPE: str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); if (!format || depth>=maxdepth) { snprintf(*bptr, limit,"%d", (int)enc); // record offset (i.e. id) return *bptr+strlen(*bptr); } else { // recursive print ptrdata = wg_decode_record(db, enc); sprint_record(db,ptrdata,buf,bufsize,bptr,format,showid,depth+1,maxdepth,strenc); **bptr='\0'; return *bptr; } break; case WG_INTTYPE: intdata = wg_decode_int(db, enc); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, "%d", intdata); return *bptr+strlen(*bptr); case WG_DOUBLETYPE: doubledata = wg_decode_double(db, enc); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, DOUBLE_FORMAT, doubledata); return *bptr+strlen(*bptr); case WG_FIXPOINTTYPE: doubledata = wg_decode_fixpoint(db, enc); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, DOUBLE_FORMAT, doubledata); return *bptr+strlen(*bptr); case WG_STRTYPE: strdata = wg_decode_str(db, enc); exdata = wg_decode_str_lang(db,enc); if (strdata!=NULL) strl1=strlen(strdata); else strl1=0; if (exdata!=NULL) strl2=strlen(exdata); else strl2=0; str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN+STRLEN_FACTOR*(strl1+strl2)); sprint_string(*bptr,(strl1+strl2),strdata,strenc); if (exdata!=NULL) { snprintf(*bptr+strl1+1,limit,"@%s\"", exdata); } return *bptr+strlen(*bptr); case WG_URITYPE: strdata = wg_decode_uri(db, enc); exdata = wg_decode_uri_prefix(db, enc); if (strdata!=NULL) strl1=strlen(strdata); else strl1=0; if (exdata!=NULL) strl2=strlen(exdata); else strl2=0; limit=MIN_STRLEN+STRLEN_FACTOR*(strl1+strl2); str_guarantee_space(buf, bufsize, bptr, limit); if (exdata==NULL) snprintf(*bptr, limit, "\"%s\"", strdata); else snprintf(*bptr, limit, "\"%s:%s\"", exdata, strdata); return *bptr+strlen(*bptr); case WG_XMLLITERALTYPE: strdata = wg_decode_xmlliteral(db, enc); exdata = wg_decode_xmlliteral_xsdtype(db, enc); if (strdata!=NULL) strl1=strlen(strdata); else strl1=0; if (exdata!=NULL) strl2=strlen(exdata); else strl2=0; limit=MIN_STRLEN+STRLEN_FACTOR*(strl1+strl2); str_guarantee_space(buf, bufsize, bptr, limit); snprintf(*bptr, limit, "\"%s:%s\"", exdata, strdata); return *bptr+strlen(*bptr); case WG_CHARTYPE: intdata = wg_decode_char(db, enc); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, "\"%c\"", (char) intdata); return *bptr+strlen(*bptr); case WG_DATETYPE: intdata = wg_decode_date(db, enc); wg_strf_iso_datetime(db,intdata,0,strbuf); strbuf[10]=0; str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, "\"%s\"",strbuf); return *bptr+strlen(*bptr); case WG_TIMETYPE: intdata = wg_decode_time(db, enc); wg_strf_iso_datetime(db,1,intdata,strbuf); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, "\"%s\"",strbuf+11); return *bptr+strlen(*bptr); case WG_VARTYPE: intdata = wg_decode_var(db, enc); str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, "\"?%d\"", intdata); return *bptr+strlen(*bptr); case WG_BLOBTYPE: strdata = wg_decode_blob(db, enc); strl=wg_decode_blob_len(db, enc); limit=MIN_STRLEN+STRLEN_FACTOR*strlen(strdata); str_guarantee_space(buf, bufsize, bptr, limit); sprint_blob(*bptr,strl,strdata,strenc); return *bptr+strlen(*bptr); default: str_guarantee_space(buf, bufsize, bptr, MIN_STRLEN); snprintf(*bptr, limit, JS_TYPE_ERR); return *bptr+strlen(*bptr); } }
void run_demo(void* db) { void *rec = NULL, *firstrec = NULL, *nextrec = NULL; /* Pointers to a database record */ wg_int enc; /* Encoded data */ wg_int lock_id; /* Id of an acquired lock (for releasing it later) */ wg_int len; int i; int intdata, datedata, timedata; char strbuf[80]; printf("********* Starting demo ************\n"); /* Begin by creating a simple record of 3 fields and fill it * with integer data. */ printf("Creating first record.\n"); rec=wg_create_record(db, 3); if (rec==NULL) { printf("rec creation error.\n"); return; } /* Encode a field, checking for errors */ enc = wg_encode_int(db, 44); if(enc==WG_ILLEGAL) { printf("failed to encode an integer.\n"); return; } /* Negative return value shows that an error occurred */ if(wg_set_field(db, rec, 0, enc) < 0) { printf("failed to store a field.\n"); return; } /* Skip error checking for the sake of brevity for the rest of fields */ enc = wg_encode_int(db, -199999); wg_set_field(db, rec, 1, enc); wg_set_field(db, rec, 2, wg_encode_int(db, 0)); /* Now examine the record we have created. Get record length, * encoded value of each field, data type and decoded value. */ /* Negative return value shows an error. */ len = wg_get_record_len(db, rec); if(len < 0) { printf("failed to get record length.\n"); return; } printf("Size of created record at %p was: %d\n", rec, (int) len); for(i=0; i<len; i++) { printf("Reading field %d:", i); enc = wg_get_field(db, rec, i); if(wg_get_encoded_type(db, enc) != WG_INTTYPE) { printf("data was of unexpected type.\n"); return; } intdata = wg_decode_int(db, enc); /* No error checking here. All integers are valid. */ printf(" %d\n", intdata); } /* Fields can be erased by setting their value to 0 which always stands for NULL value. */ printf("Clearing field 1.\n"); wg_set_field(db, rec, 1, 0); if(wg_get_field(db, rec, 1)==0) { printf("Re-reading field 1 returned a 0 (NULL) field.\n"); } else { printf("unexpected value \n"); return; } /* Fields can be updated with data of any type (the type is not fixed). */ printf("Updating field 0 to a floating-point number.\n"); enc = wg_encode_double(db, 56.9988); wg_set_field(db, rec, 0, enc); enc = wg_get_field(db, rec, 0); if(wg_get_encoded_type(db, enc) == WG_DOUBLETYPE) { printf("Re-reading field 0 returned %f.\n", wg_decode_double(db, enc)); } else { printf("data was of unexpected type.\n"); return; } /* Create a next record. Let's assume we're in an environment where * the database is used concurrently, so there's a need to use locking. */ printf("Creating second record.\n"); /* Lock id of 0 means that the operation failed */ lock_id = wg_start_write(db); if(!lock_id) { printf("failed to acquire lock.\n"); return; } /* Do the write operation we acquired the lock for. */ rec=wg_create_record(db, 6); /* Failing to release the lock would be fatal to database operation. */ if(!wg_end_write(db, lock_id)) { printf("failed to release lock.\n"); return; } if (!rec) { printf("rec creation error.\n"); return; } /* Reading also requires locking./ */ lock_id = wg_start_read(db); if(!lock_id) { printf("failed to acquire lock.\n"); return; } /* Do our read operation... */ len = wg_get_record_len(db, rec); /* ... and unlock immediately */ if(!wg_end_read(db, lock_id)) { printf("failed to release lock.\n"); return; } if(len < 0) { printf("failed to get record length.\n"); return; } printf("Size of created record at %p was: %d\n", rec, (int) len); /* Let's find the first record in the database */ lock_id = wg_start_read(db); firstrec = wg_get_first_record(db); wg_end_read(db, lock_id); if(!firstrec) { printf("Failed to find first record.\n"); return; } printf("First record of database had address %p.\n", firstrec); /* Let's check what the next record is to demonstrate scanning records. */ nextrec = firstrec; lock_id = wg_start_read(db); do { nextrec = wg_get_next_record(db, nextrec); if(nextrec) printf("Next record had address %p.\n", nextrec); } while(nextrec); printf("Finished scanning database records.\n"); wg_end_read(db, lock_id); /* Set fields to various values. Field 0 is not touched at all (un- * initialized). Field 1 is set to point to another record. */ printf("Populating second record with data.\n"); /* Let's use the first record we found to demonstrate storing * a link to a record in a field inside another record. */ lock_id = wg_start_write(db); enc = wg_encode_record(db, firstrec); wg_set_field(db, rec, 1, enc); wg_end_write(db, lock_id); /* Now set other fields to various data types. To keep the example shorter, * the locking and unlocking operations are omitted (in real applications, * this would be incorrect usage if concurrent access is expected). */ wg_set_field(db, rec, 2, wg_encode_str(db, "This is a char array", NULL)); wg_set_field(db, rec, 3, wg_encode_char(db, 'a')); /* For time and date, we use current time in local timezone */ enc = wg_encode_date(db, wg_current_localdate(db)); if(enc==WG_ILLEGAL) { printf("failed to encode date.\n"); return; } wg_set_field(db, rec, 4, enc); enc = wg_encode_time(db, wg_current_localtime(db)); if(enc==WG_ILLEGAL) { printf("failed to encode time.\n"); return; } wg_set_field(db, rec, 5, enc); /* Now read and print all the fields. */ wg_print_record(db, (wg_int *) rec); printf("\n"); /* Date and time can be handled together as a datetime object. */ datedata = wg_decode_date(db, wg_get_field(db, rec, 4)); timedata = wg_decode_time(db, wg_get_field(db, rec, 5)); wg_strf_iso_datetime(db, datedata, timedata, strbuf); printf("Reading datetime: %s.\n", strbuf); printf("Setting date and time to 2010-03-31, 12:59\n"); /* Update date and time to arbitrary values using wg_strp_iso_date/time */ wg_set_field(db, rec, 4, wg_encode_date(db, wg_strp_iso_date(db, "2010-03-31"))); wg_set_field(db, rec, 5, wg_encode_time(db, wg_strp_iso_time(db, "12:59:00.33"))); printf("Dumping the contents of the database:\n"); wg_print_db(db); printf("********* Demo ended ************\n"); }
/* * Return an encoded value as a decoded byte array. * It should be freed afterwards. * returns the number of bytes in the array. * returns 0 if the decode failed. * * NOTE: to differentiate between identical byte strings * the value is prefixed with a type identifier. * TODO: For values with varying length that can contain * '\0' bytes, add length to the prefix. */ gint wg_decode_for_hashing(void *db, gint enc, char **decbytes) { gint len; gint type; gint ptrdata; int intdata; double doubledata; char *bytedata; char *exdata, *buf = NULL, *outbuf; type = wg_get_encoded_type(db, enc); switch(type) { case WG_NULLTYPE: len = sizeof(gint); ptrdata = 0; bytedata = (char *) &ptrdata; break; case WG_RECORDTYPE: len = sizeof(gint); ptrdata = enc; bytedata = (char *) &ptrdata; break; case WG_INTTYPE: len = sizeof(int); intdata = wg_decode_int(db, enc); bytedata = (char *) &intdata; break; case WG_DOUBLETYPE: len = sizeof(double); doubledata = wg_decode_double(db, enc); bytedata = (char *) &doubledata; break; case WG_FIXPOINTTYPE: len = sizeof(double); doubledata = wg_decode_fixpoint(db, enc); bytedata = (char *) &doubledata; break; case WG_STRTYPE: len = wg_decode_str_len(db, enc); bytedata = wg_decode_str(db, enc); break; case WG_URITYPE: len = wg_decode_uri_len(db, enc); bytedata = wg_decode_uri(db, enc); exdata = wg_decode_uri_prefix(db, enc); CONCAT_FOR_HASHING(db, bytedata, exdata, len, buf, enc) break; case WG_XMLLITERALTYPE: len = wg_decode_xmlliteral_len(db, enc); bytedata = wg_decode_xmlliteral(db, enc); exdata = wg_decode_xmlliteral_xsdtype(db, enc); CONCAT_FOR_HASHING(db, bytedata, exdata, len, buf, enc) break; case WG_CHARTYPE: len = sizeof(int); intdata = wg_decode_char(db, enc); bytedata = (char *) &intdata; break; case WG_DATETYPE: len = sizeof(int); intdata = wg_decode_date(db, enc); bytedata = (char *) &intdata; break; case WG_TIMETYPE: len = sizeof(int); intdata = wg_decode_time(db, enc); bytedata = (char *) &intdata; break; case WG_VARTYPE: len = sizeof(int); intdata = wg_decode_var(db, enc); bytedata = (char *) &intdata; break; case WG_ANONCONSTTYPE: /* Ignore anonconst */ default: return 0; } /* Form the hashable buffer. It is not 0-terminated */ outbuf = malloc(len + 1); if(outbuf) { outbuf[0] = (char) type; memcpy(outbuf + 1, bytedata, len++); *decbytes = outbuf; } else { /* Indicate failure */ len = 0; } if(buf) free(buf); return len; }