summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2011-12-07 23:11:19 +0100
committerHicks James <jamey.hicks@nokia.com>2011-12-08 15:16:57 +0100
commitd168fcbfdc0e9d5357209a0e7f06a6b8c02308a8 (patch)
treecff1c3ee645a26cb02403efd7fcc39752a1addf6
parent2bfe7d515f3eed642b53f5e9b93e3fb676cc2b3b (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.h81
-rw-r--r--src/qjsonobject.cpp37
-rw-r--r--src/qjsonvalue.cpp13
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: