diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2011-12-08 10:24:16 +0100 |
---|---|---|
committer | Simons Kevin <kevin.simons@nokia.com> | 2011-12-08 15:27:52 +0100 |
commit | a352643165b7d015695747fc31e60983c336d783 (patch) | |
tree | 3b3feb17c9f1b2502708f45936fd099c3971ac14 | |
parent | 3105e87528a0ba786ef6b15265dec5e597317dac (diff) |
Store smaller integers inline
Integers using less than 28 bits will now
be stored inline in the Value, saving 8 bytes
of space in the binary structure.
Change-Id: If34b6bd10401235ef70117956f79f5dc39fed05a
Reviewed-by: Simons Kevin <kevin.simons@nokia.com>
-rw-r--r-- | src/qjson_p.h | 83 | ||||
-rw-r--r-- | src/qjsonvalue.cpp | 13 | ||||
-rw-r--r-- | tests/auto/tst_qtjson.cpp | 71 |
3 files changed, 148 insertions, 19 deletions
diff --git a/src/qjson_p.h b/src/qjson_p.h index f2446f3..42f0d1a 100644 --- a/src/qjson_p.h +++ b/src/qjson_p.h @@ -32,7 +32,7 @@ { // object: 16 + 5*12 = 72 "firstName": "John", // key 4+12, value 8 = 24 "lastName" : "Smith", // key 4+8, value 8 = 20 - "age" : 25, // key 4+4, value 8 = 16 + "age" : 25, // key 4+4, value 0 = 8 "address" : // key 4+8, object below = 160 { // object: 16 + 4*12 "streetAddress": "21 2nd Street", // key 4+16, value 16 @@ -51,7 +51,7 @@ "number": "646 555-4567" // key 4+8, value 16 } // object total: 84 ] // array total: 248 - } // great total: 500 bytes + } // great total: 492 bytes The uncompressed text file used roughly 500 bytes, so we end up using about the same space as the text representation. @@ -88,6 +88,35 @@ static inline void copyString(char *dest, const QString &str, bool compress) } } + +// returns INT_MAX if it can't compress it into 28 bits +static inline int compressedNumber(double d) +{ + // this relies on details of how ieee floats are represented + const int exponent_off = 52; + const quint64 fraction_mask = 0x000fffffffffffff; + const quint64 exponent_mask = 0x7ff0000000000000; + + union { + quint64 val; + double dbl; + }; + dbl = d; + int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; + if (exp < 0 || exp > 26) + return INT_MAX; + + quint64 non_int = val & (fraction_mask >> exp); + if (non_int) + return INT_MAX; + + bool neg = (val >> 63); + val &= fraction_mask; + val |= ((quint64)1 << 52); + int res = (int)(val >> (52 - exp)); + return neg ? -res : res; +} + struct Latin1String; struct String @@ -249,15 +278,26 @@ struct Array : public Base struct Value { - uint val : 28; - uint compressed : 1; - uint type : 3; + // unfortunately some compilers can't handle mixed types in bitfields. + // this works around the problem + union { + struct { + int int_val : 28; + int unused : 4; + }; + struct { + uint val : 28; + uint compressed : 1; + uint type : 3; + }; + }; + inline char *data(const Base *b) const { return ((char *)b) + val; } bool toBoolean() const; double toNumber(const Base *b) const; -// int toInt() const; + int toInt() const; QString toString(const Base *b) const; String asString(const Base *b) const; Latin1String asLatin1String(const Base *b) const; @@ -276,11 +316,12 @@ struct Value type = BooleanValue; } -// inline void setInt(int i) { -// val = i; -// compressed = true; -// type = NumberValue; -// } + inline void setInt(int i) { + Q_ASSERT(i < 1e27 && i > -1e27); + int_val = i; + compressed = true; + type = NumberValue; + } inline void setString(Base *b, offset off, const QString &str) { val = off; @@ -362,14 +403,22 @@ inline bool Value::toBoolean() const inline double Value::toNumber(const Base *b) const { Q_ASSERT(type == NumberValue); - return *(double *)((char *)b + val); + if (compressed) + return int_val; + + union { + char raw[sizeof(double)]; + double d; + }; + memcpy(raw, ((char *)b + val), sizeof(double)); + return d; } -//inline int Value::toInt() const -//{ -// Q_ASSERT(type == NumberValue); -// return (int)*(double *)((char *)(this+1)); -//} +inline int Value::toInt() const +{ + Q_ASSERT(type == NumberValue && compressed); + return int_val; +} inline QString Value::toString(const Base *b) const { diff --git a/src/qjsonvalue.cpp b/src/qjsonvalue.cpp index 15d248a..0028728 100644 --- a/src/qjsonvalue.cpp +++ b/src/qjsonvalue.cpp @@ -330,6 +330,10 @@ int JsonValue::requiredStorage(bool *compressed) const *compressed = false; switch (t) { case NumberValue: + if (compressedNumber(dbl) != INT_MAX) { + *compressed = true; + return 0; + } return sizeof(double); case StringValue: if (stringData->size < 0x8000) { @@ -366,7 +370,11 @@ uint JsonValue::valueToStore(uint offset) const break; case BooleanValue: return b; - case NumberValue: + case NumberValue: { + int c = compressedNumber(dbl); + if (c != INT_MAX) + return c; + } case StringValue: case ArrayValue: case ObjectValue: @@ -379,7 +387,8 @@ void JsonValue::copyData(char *dest, bool compressed) const { switch (t) { case NumberValue: - memcpy(dest, raw, sizeof(dbl)); + if (!compressed) + memcpy(dest, raw, sizeof(dbl)); break; case StringValue: { QString str = toString(); diff --git a/tests/auto/tst_qtjson.cpp b/tests/auto/tst_qtjson.cpp index 8926a4c..f13dbee 100644 --- a/tests/auto/tst_qtjson.cpp +++ b/tests/auto/tst_qtjson.cpp @@ -58,6 +58,8 @@ private Q_SLOTS: void cleanup(); void testValueSimple(); + void testNumbers(); + void testObjectSimple(); void testArraySimple(); void testValueObject(); @@ -147,6 +149,75 @@ void TestQtJson::testValueSimple() } +void TestQtJson::testNumbers() +{ + { + int numbers[] = { + 0, + -1, + 1, + (1<<26), + (1<<27), + (1<<28), + -(1<<26), + -(1<<27), + -(1<<28), + (1<<26) - 1, + (1<<27) - 1, + (1<<28) - 1, + -((1<<26) - 1), + -((1<<27) - 1), + -((1<<28) - 1) + }; + int n = sizeof(numbers)/sizeof(int); + + JsonArray array; + for (int i = 0; i < n; ++i) + array.append(numbers[i]); + for (int i = 0; i < array.size(); ++i) { + QCOMPARE(array.at(i).type(), NumberValue); + QCOMPARE(array.at(i).toInt(), numbers[i]); + QCOMPARE(array.at(i).toNumber(), (double)numbers[i]); + } + } + + { + double numbers[] = { + 0, + -1, + 1, + (1<<26), + (1<<27), + (1<<28), + -(1<<26), + -(1<<27), + -(1<<28), + (1<<26) - 1, + (1<<27) - 1, + (1<<28) - 1, + -((1<<26) - 1), + -((1<<27) - 1), + -((1<<28) - 1), + 1.1, + 0.1, + -0.1, + -1.1, + 1e200, + -1e200 + }; + int n = sizeof(numbers)/sizeof(double); + + JsonArray array; + for (int i = 0; i < n; ++i) + array.append(numbers[i]); + for (int i = 0; i < array.size(); ++i) { + QCOMPARE(array.at(i).type(), NumberValue); + QCOMPARE(array.at(i).toNumber(), numbers[i]); + } + } + +} + void TestQtJson::testObjectSimple() { JsonObject object; |