diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2011-12-07 23:11:19 +0100 |
---|---|---|
committer | Hicks James <jamey.hicks@nokia.com> | 2011-12-08 15:16:57 +0100 |
commit | d168fcbfdc0e9d5357209a0e7f06a6b8c02308a8 (patch) | |
tree | cff1c3ee645a26cb02403efd7fcc39752a1addf6 | |
parent | 2bfe7d515f3eed642b53f5e9b93e3fb676cc2b3b (diff) |
Use latin1 for keys as well
Store keys as latin1 strings as well if possible.
This brings the memory usage down to the same size
as the text representation.
Change-Id: I6f9eae11d576122cf2e1e31d78ec961cdfd7019c
Reviewed-by: Hicks James <jamey.hicks@nokia.com>
-rw-r--r-- | src/qjson_p.h | 81 | ||||
-rw-r--r-- | src/qjsonobject.cpp | 37 | ||||
-rw-r--r-- | src/qjsonvalue.cpp | 13 |
3 files changed, 89 insertions, 42 deletions
diff --git a/src/qjson_p.h b/src/qjson_p.h index 47da9e4..f2446f3 100644 --- a/src/qjson_p.h +++ b/src/qjson_p.h @@ -30,30 +30,31 @@ For an example such as { // object: 16 + 5*12 = 72 - "firstName": "John", // key 4+18, value 4+4+8 = 38 - "lastName" : "Smith", // key 4+16, value 4+4+10 = 38 - "age" : 25, // key 4+6, value 4+8 = 22 - "address" : // key 4+14 value 4 + object below = 288 + "firstName": "John", // key 4+12, value 8 = 24 + "lastName" : "Smith", // key 4+8, value 8 = 20 + "age" : 25, // key 4+4, value 8 = 16 + "address" : // key 4+8, object below = 160 { // object: 16 + 4*12 - "streetAddress": "21 2nd Street", // key 4+26, value 4+4+26 - "city" : "New York", // key 4+8, value 4+4+16 - "state" : "NY", // key 4+10, value 4+4+4 - "postalCode" : "10021" // key 4+20, value 4+4+10 - }, // object total: 266 - "phoneNumber": // key: 4+22, value 4 + array below = 278 - [ // array: 16 + 2*4 + values below - { // value 4 + object 16 + 2*12 - "type" : "home", // key 4+8, value 4+4+8 - "number": "212 555-1234" // key 4+12, value 4+4+26 - }, // object total: 112 - { // value 4 + object 16 + 2*12 - "type" : "fax", // key 4+6, value 4+4+6 - "number": "646 555-4567" // key 4+12, value 4+4+26 - } // object total: 110 + "streetAddress": "21 2nd Street", // key 4+16, value 16 + "city" : "New York", // key 4+4, value 12 + "state" : "NY", // key 4+8, value 4 + "postalCode" : "10021" // key 4+12, value 8 + }, // object total: 148 + "phoneNumber": // key: 4+12, value array below = 208 + [ // array: 16 + 2*4 + values below = 192 + { // object 16 + 2*12 + "type" : "home", // key 4+4, value 8 + "number": "212 555-1234" // key 4+8, value 16 + }, // object total: 84 + { // object 16 + 2*12 + "type" : "fax", // key 4+4, value 8 + "number": "646 555-4567" // key 4+8, value 16 + } // object total: 84 ] // array total: 248 - } // great total: 736 bytes + } // great total: 500 bytes - we end up using roughly 1kb. The uncompressed text file used roughly 500 bytes. + The uncompressed text file used roughly 500 bytes, so we end up using about the same space + as the text representation. */ namespace QtJson { @@ -64,11 +65,27 @@ struct Object; struct Value; struct Entry; -static inline int alignedSize(int size) { return (size + 7) & ~7; } +static inline int alignedSize(int size) { return (size + 3) & ~3; } -static inline int qStringSize(const QString &string) +static inline int qStringSize(const QString &string, bool compress) { - return (sizeof(ushort)*string.length() + 3) & ~3; + if (compress) + return alignedSize(string.length()); + return alignedSize(sizeof(ushort)*string.length()); +} + +static inline void copyString(char *dest, const QString &str, bool compress) +{ + if (compress) { + *((ushort *)dest) = str.length(); + uchar *d = (uchar *)dest + sizeof(ushort); + const ushort *uc = (const ushort *)str.unicode(); + for (int i = 0; i < str.length(); ++i) + *d++ = uc[i]; + } else { + *((int *)dest) = str.length(); + memcpy(dest + sizeof(int), str.constData(), str.length()*sizeof(ushort)); + } } struct Latin1String; @@ -294,14 +311,19 @@ inline Value &Array::operator [](int i) struct Entry { uint size; Value value; - uint keyLength; + uint compressed : 1; + uint keyLength : 31; ushort *keyData() const { return (ushort *)(this + 1); } + uchar *latin1KeyData() const { return (uchar *)(this + 1); } // keydata // value data follows key data String shallowKey() const { return String(keyData(), keyLength); } + Latin1String shallowLatin1Key() const { return Latin1String(latin1KeyData(), keyLength); } QString key() const { + if (compressed) + return QString::fromLatin1((const char *)latin1KeyData(), keyLength); return QString((const QChar *)keyData(), keyLength); } }; @@ -310,8 +332,13 @@ inline int Object::indexOf(const QString &key) { for (int i = 0; i < (int)length; ++i) { Entry *e = entryAt(i); - if (e->shallowKey() == key) - return i; + if (e->compressed) { + if (e->shallowLatin1Key() == key) + return i; + } else { + if (e->shallowKey() == key) + return i; + } } return -1; } diff --git a/src/qjsonobject.cpp b/src/qjsonobject.cpp index c975dd9..0b02dea 100644 --- a/src/qjsonobject.cpp +++ b/src/qjsonobject.cpp @@ -128,18 +128,38 @@ JsonValue JsonObject::value(const QString &key) const for (uint i = 0; i < o->length; ++i) { Entry *e = o->entryAt(i); - if (e->shallowKey() == key) - return JsonValue(d, o, e->value); + if (e->compressed) { + if (e->shallowLatin1Key() == key) + return JsonValue(d, o, e->value); + } else { + if (e->shallowKey() == key) + return JsonValue(d, o, e->value); + } } return JsonValue(); } +static bool useCompressed(const QString &s) +{ + if (s.length() >= 0x8000) + return false; + const ushort *uc = (const ushort *)s.constData(); + const ushort *e = uc + s.length(); + while (uc < e) { + if (*uc > 0xff) + return false; + ++uc; + } + return true; +} + void JsonObject::insert(const QString &key, const JsonValue &value) { bool compressed; int valueSize = value.requiredStorage(&compressed); - int valueOffset = sizeof(Entry) + qStringSize(key); + bool compress = useCompressed(key); + int valueOffset = sizeof(Entry) + qStringSize(key, compress); int requiredSize = valueOffset + valueSize; detach(requiredSize + sizeof(offset)); // offset for the new index entry @@ -155,7 +175,16 @@ void JsonObject::insert(const QString &key, const JsonValue &value) Entry *e = o->entryAt(pos); e->size = requiredSize; e->keyLength = key.length(); - memcpy(e->keyData(), key.constData(), sizeof(ushort)*key.length()); + e->compressed = compress; + if (compress) { + uchar *c = e->latin1KeyData(); + const ushort *uc = (const ushort *)key.constData(); + const ushort *e = uc + key.length(); + while (uc < e) + *c++ = *uc++; + } else { + memcpy(e->keyData(), key.constData(), sizeof(ushort)*key.length()); + } e->value.type = value.t; e->value.compressed = compressed; e->value.val = value.valueToStore(d->offsetOf(e) + valueOffset); diff --git a/src/qjsonvalue.cpp b/src/qjsonvalue.cpp index 08c3d08..2527500 100644 --- a/src/qjsonvalue.cpp +++ b/src/qjsonvalue.cpp @@ -342,7 +342,7 @@ int JsonValue::requiredStorage(bool *compressed) const ++uc; } } - return *compressed ? sizeof(ushort) + stringData->size : sizeof(uint) + sizeof(ushort)*stringData->size; + return alignedSize(*compressed ? sizeof(ushort) + stringData->size : sizeof(uint) + sizeof(ushort)*stringData->size); case ArrayValue: return array ? array->size : sizeof(Array); case ObjectValue: @@ -378,16 +378,7 @@ void JsonValue::copyData(char *dest, bool compressed) const break; case StringValue: { QString str = toString(); - if (compressed) { - *((ushort *)dest) = str.length(); - uchar *d = (uchar *)dest + sizeof(ushort); - const ushort *uc = (const ushort *)str.unicode(); - for (int i = 0; i < str.length(); ++i) - *d++ = uc[i]; - } else { - *((int *)dest) = str.length(); - memcpy(dest + sizeof(int), str.constData(), str.length()*sizeof(ushort)); - } + copyString(dest, str, compressed); break; } case ArrayValue: |