summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2011-12-08 10:24:16 +0100
committerSimons Kevin <kevin.simons@nokia.com>2011-12-08 15:27:52 +0100
commita352643165b7d015695747fc31e60983c336d783 (patch)
tree3b3feb17c9f1b2502708f45936fd099c3971ac14
parent3105e87528a0ba786ef6b15265dec5e597317dac (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.h83
-rw-r--r--src/qjsonvalue.cpp13
-rw-r--r--tests/auto/tst_qtjson.cpp71
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;