You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2015/05/12 20:01:37 UTC

[2/2] lucy git commit: Copy NumberUtils from Clownfish to Lucy

Copy NumberUtils from Clownfish to Lucy


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/8782bd56
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/8782bd56
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/8782bd56

Branch: refs/heads/master
Commit: 8782bd56b4d204116400cc9729ad5de11260bb3d
Parents: 51825fd
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Sat May 9 17:50:14 2015 +0200
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Tue May 12 19:55:15 2015 +0200

----------------------------------------------------------------------
 core/Lucy/Index/DocVector.c                |   1 +
 core/Lucy/Index/HighlightWriter.c          |   1 +
 core/Lucy/Index/LexIndex.c                 |   1 +
 core/Lucy/Index/Posting/RichPosting.c      |   1 +
 core/Lucy/Index/Posting/ScorePosting.c     |   1 +
 core/Lucy/Index/SortCache.c                |   1 +
 core/Lucy/Index/SortFieldWriter.c          |   1 +
 core/Lucy/Object/BitVector.c               |   1 +
 core/Lucy/Search/Collector/SortCollector.c |   1 +
 core/Lucy/Store/InStream.c                 |   1 +
 core/Lucy/Store/OutStream.c                |   1 +
 core/Lucy/Test.c                           |   2 +
 core/Lucy/Test/Store/TestIOChunks.c        |   1 -
 core/Lucy/Test/Store/TestIOPrimitives.c    |   2 +-
 core/Lucy/Test/Store/TestInStream.c        |   1 -
 core/Lucy/Test/Util/TestNumberUtils.c      | 375 +++++++++++++++++++
 core/Lucy/Test/Util/TestNumberUtils.cfh    |  29 ++
 core/Lucy/Util/NumberUtils.cfh             | 466 ++++++++++++++++++++++++
 core/Lucy/Util/ToolSet.h                   |   1 -
 perl/t/core/030-number_utils.t             |  25 ++
 20 files changed, 909 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/DocVector.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/DocVector.c b/core/Lucy/Index/DocVector.c
index cc08125..4868890 100644
--- a/core/Lucy/Index/DocVector.c
+++ b/core/Lucy/Index/DocVector.c
@@ -25,6 +25,7 @@
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Store/OutStream.h"
 #include "Lucy/Util/Freezer.h"
+#include "Lucy/Util/NumberUtils.h"
 
 // Extract a document's compressed TermVector data into (term_text =>
 // compressed positional data) pairs.

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/HighlightWriter.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/HighlightWriter.c b/core/Lucy/Index/HighlightWriter.c
index fbb4e91..a8115ae 100644
--- a/core/Lucy/Index/HighlightWriter.c
+++ b/core/Lucy/Index/HighlightWriter.c
@@ -37,6 +37,7 @@
 #include "Lucy/Store/OutStream.h"
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Util/Freezer.h"
+#include "Lucy/Util/NumberUtils.h"
 
 static OutStream*
 S_lazy_init(HighlightWriter *self);

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/LexIndex.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/LexIndex.c b/core/Lucy/Index/LexIndex.c
index 7cbe022..f9f6064 100644
--- a/core/Lucy/Index/LexIndex.c
+++ b/core/Lucy/Index/LexIndex.c
@@ -26,6 +26,7 @@
 #include "Lucy/Plan/Schema.h"
 #include "Lucy/Store/Folder.h"
 #include "Lucy/Store/InStream.h"
+#include "Lucy/Util/NumberUtils.h"
 
 // Read the data we've arrived at after a seek operation.
 static void

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/Posting/RichPosting.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/Posting/RichPosting.c b/core/Lucy/Index/Posting/RichPosting.c
index a008514..17e3e95 100644
--- a/core/Lucy/Index/Posting/RichPosting.c
+++ b/core/Lucy/Index/Posting/RichPosting.c
@@ -31,6 +31,7 @@
 #include "Lucy/Search/Compiler.h"
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Util/MemoryPool.h"
+#include "Lucy/Util/NumberUtils.h"
 
 #define FREQ_MAX_LEN     C32_MAX_BYTES
 #define MAX_RAW_POSTING_LEN(_raw_posting_size, _text_len, _freq) \

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/Posting/ScorePosting.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/Posting/ScorePosting.c b/core/Lucy/Index/Posting/ScorePosting.c
index 18b4692..3e995a8 100644
--- a/core/Lucy/Index/Posting/ScorePosting.c
+++ b/core/Lucy/Index/Posting/ScorePosting.c
@@ -32,6 +32,7 @@
 #include "Lucy/Search/Matcher.h"
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Util/MemoryPool.h"
+#include "Lucy/Util/NumberUtils.h"
 
 #define FIELD_BOOST_LEN  1
 #define FREQ_MAX_LEN     C32_MAX_BYTES

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/SortCache.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/SortCache.c b/core/Lucy/Index/SortCache.c
index c451da6..fe25049 100644
--- a/core/Lucy/Index/SortCache.c
+++ b/core/Lucy/Index/SortCache.c
@@ -19,6 +19,7 @@
 
 #include "Lucy/Index/SortCache.h"
 #include "Lucy/Plan/FieldType.h"
+#include "Lucy/Util/NumberUtils.h"
 
 SortCache*
 SortCache_init(SortCache *self, String *field, FieldType *type,

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Index/SortFieldWriter.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Index/SortFieldWriter.c b/core/Lucy/Index/SortFieldWriter.c
index b40fae7..d63bed2 100644
--- a/core/Lucy/Index/SortFieldWriter.c
+++ b/core/Lucy/Index/SortFieldWriter.c
@@ -35,6 +35,7 @@
 #include "Lucy/Store/Folder.h"
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Store/OutStream.h"
+#include "Lucy/Util/NumberUtils.h"
 #include "Clownfish/Blob.h"
 #include "Clownfish/Util/Memory.h"
 #include "Clownfish/Util/SortUtils.h"

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Object/BitVector.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Object/BitVector.c b/core/Lucy/Object/BitVector.c
index efc7278..8b7dd9e 100644
--- a/core/Lucy/Object/BitVector.c
+++ b/core/Lucy/Object/BitVector.c
@@ -20,6 +20,7 @@
 #include <math.h>
 
 #include "Lucy/Object/BitVector.h"
+#include "Lucy/Util/NumberUtils.h"
 
 // Shared subroutine for performing both OR and XOR ops.
 #define DO_OR 1

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Search/Collector/SortCollector.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Search/Collector/SortCollector.c b/core/Lucy/Search/Collector/SortCollector.c
index 5354642..e58e80a 100644
--- a/core/Lucy/Search/Collector/SortCollector.c
+++ b/core/Lucy/Search/Collector/SortCollector.c
@@ -33,6 +33,7 @@
 #include "Lucy/Search/Matcher.h"
 #include "Lucy/Search/SortRule.h"
 #include "Lucy/Search/SortSpec.h"
+#include "Lucy/Util/NumberUtils.h"
 
 #define COMPARE_BY_SCORE             0x1
 #define COMPARE_BY_SCORE_REV         0x2

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Store/InStream.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Store/InStream.c b/core/Lucy/Store/InStream.c
index 43881e8..fd5b36f 100644
--- a/core/Lucy/Store/InStream.c
+++ b/core/Lucy/Store/InStream.c
@@ -25,6 +25,7 @@
 #include "Lucy/Store/FileWindow.h"
 #include "Lucy/Store/RAMFile.h"
 #include "Lucy/Store/RAMFileHandle.h"
+#include "Lucy/Util/NumberUtils.h"
 
 // Inlined version of InStream_Tell.
 static CFISH_INLINE int64_t

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Store/OutStream.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Store/OutStream.c b/core/Lucy/Store/OutStream.c
index f61466e..ca4e676 100644
--- a/core/Lucy/Store/OutStream.c
+++ b/core/Lucy/Store/OutStream.c
@@ -26,6 +26,7 @@
 #include "Lucy/Store/InStream.h"
 #include "Lucy/Store/RAMFile.h"
 #include "Lucy/Store/RAMFileHandle.h"
+#include "Lucy/Util/NumberUtils.h"
 
 // Inlined version of OutStream_Write_Bytes.
 static CFISH_INLINE void

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Test.c b/core/Lucy/Test.c
index 6817911..3046494 100644
--- a/core/Lucy/Test.c
+++ b/core/Lucy/Test.c
@@ -81,6 +81,7 @@
 #include "Lucy/Test/Util/TestIndexFileNames.h"
 #include "Lucy/Test/Util/TestJson.h"
 #include "Lucy/Test/Util/TestMemoryPool.h"
+#include "Lucy/Test/Util/TestNumberUtils.h"
 #include "Lucy/Test/Util/TestPriorityQueue.h"
 #include "Lucy/Test/Util/TestSortExternal.h"
 
@@ -92,6 +93,7 @@ Test_create_test_suite() {
     TestSuite_Add_Batch(suite, (TestBatch*)TestBitVector_new());
     TestSuite_Add_Batch(suite, (TestBatch*)TestSortExternal_new());
     TestSuite_Add_Batch(suite, (TestBatch*)TestMemPool_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestNumUtil_new());
     TestSuite_Add_Batch(suite, (TestBatch*)TestIxFileNames_new());
     TestSuite_Add_Batch(suite, (TestBatch*)TestJson_new());
     TestSuite_Add_Batch(suite, (TestBatch*)TestFreezer_new());

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test/Store/TestIOChunks.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Test/Store/TestIOChunks.c b/core/Lucy/Test/Store/TestIOChunks.c
index c09d665..bf35d0b 100644
--- a/core/Lucy/Test/Store/TestIOChunks.c
+++ b/core/Lucy/Test/Store/TestIOChunks.c
@@ -33,7 +33,6 @@
 #include "Lucy/Store/OutStream.h"
 #include "Lucy/Store/RAMFile.h"
 #include "Lucy/Store/RAMFileHandle.h"
-#include "Clownfish/Util/NumberUtils.h"
 
 TestIOChunks*
 TestIOChunks_new() {

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test/Store/TestIOPrimitives.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Test/Store/TestIOPrimitives.c b/core/Lucy/Test/Store/TestIOPrimitives.c
index 30008d6..551f1ac 100644
--- a/core/Lucy/Test/Store/TestIOPrimitives.c
+++ b/core/Lucy/Test/Store/TestIOPrimitives.c
@@ -33,7 +33,7 @@
 #include "Lucy/Store/OutStream.h"
 #include "Lucy/Store/RAMFile.h"
 #include "Lucy/Store/RAMFileHandle.h"
-#include "Clownfish/Util/NumberUtils.h"
+#include "Lucy/Util/NumberUtils.h"
 
 TestIOPrimitives*
 TestIOPrimitives_new() {

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test/Store/TestInStream.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Test/Store/TestInStream.c b/core/Lucy/Test/Store/TestInStream.c
index 248e4f2..764fa83 100644
--- a/core/Lucy/Test/Store/TestInStream.c
+++ b/core/Lucy/Test/Store/TestInStream.c
@@ -30,7 +30,6 @@
 #include "Lucy/Store/OutStream.h"
 #include "Lucy/Store/RAMFile.h"
 #include "Lucy/Store/RAMFileHandle.h"
-#include "Clownfish/Util/NumberUtils.h"
 
 TestInStream*
 TestInStream_new() {

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test/Util/TestNumberUtils.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Test/Util/TestNumberUtils.c b/core/Lucy/Test/Util/TestNumberUtils.c
new file mode 100644
index 0000000..c639daf
--- /dev/null
+++ b/core/Lucy/Test/Util/TestNumberUtils.c
@@ -0,0 +1,375 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+
+#define TESTLUCY_USE_SHORT_NAMES
+#include "Lucy/Util/ToolSet.h"
+
+#include "charmony.h"
+
+#include "Lucy/Test/Util/TestNumberUtils.h"
+
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Lucy/Util/NumberUtils.h"
+
+TestNumberUtils*
+TestNumUtil_new() {
+    return (TestNumberUtils*)Class_Make_Obj(TESTNUMBERUTILS);
+}
+
+static void
+test_u1(TestBatchRunner *runner) {
+    size_t    count   = 64;
+    uint64_t *ints    = TestUtils_random_u64s(NULL, count, 0, 2);
+    size_t    amount  = count / 8;
+    uint8_t  *bits    = (uint8_t*)CALLOCATE(amount, sizeof(uint8_t));
+
+    for (size_t i = 0; i < count; i++) {
+        if (ints[i]) { NumUtil_u1set(bits, i); }
+    }
+    for (size_t i = 0; i < count; i++) {
+        TEST_INT_EQ(runner, NumUtil_u1get(bits, i), (long)ints[i],
+                    "u1 set/get");
+    }
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_u1flip(bits, i);
+    }
+    for (size_t i = 0; i < count; i++) {
+        TEST_INT_EQ(runner, NumUtil_u1get(bits, i), !ints[i], "u1 flip");
+    }
+
+    FREEMEM(bits);
+    FREEMEM(ints);
+}
+
+static void
+test_u2(TestBatchRunner *runner) {
+    size_t    count = 32;
+    uint64_t *ints = TestUtils_random_u64s(NULL, count, 0, 4);
+    uint8_t  *bits = (uint8_t*)CALLOCATE((count / 4), sizeof(uint8_t));
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_u2set(bits, i, (uint8_t)ints[i]);
+    }
+    for (size_t i = 0; i < count; i++) {
+        TEST_INT_EQ(runner, NumUtil_u2get(bits, i), (long)ints[i], "u2");
+    }
+
+    FREEMEM(bits);
+    FREEMEM(ints);
+}
+
+static void
+test_u4(TestBatchRunner *runner) {
+    size_t    count = 128;
+    uint64_t *ints  = TestUtils_random_u64s(NULL, count, 0, 16);
+    uint8_t  *bits  = (uint8_t*)CALLOCATE((count / 2), sizeof(uint8_t));
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_u4set(bits, i, (uint8_t)ints[i]);
+    }
+    for (size_t i = 0; i < count; i++) {
+        TEST_INT_EQ(runner, NumUtil_u4get(bits, i), (long)ints[i], "u4");
+    }
+
+    FREEMEM(bits);
+    FREEMEM(ints);
+}
+
+static void
+test_c32(TestBatchRunner *runner) {
+    uint64_t  mins[]   = { 0,   0x4000 - 100, (uint32_t)INT32_MAX - 100, UINT32_MAX - 10 };
+    uint64_t  limits[] = { 500, 0x4000 + 100, (uint32_t)INT32_MAX + 100, UINT32_MAX      };
+    uint32_t  set_num;
+    uint32_t  num_sets  = sizeof(mins) / sizeof(uint64_t);
+    size_t    count     = 64;
+    uint64_t *ints      = NULL;
+    size_t    amount    = count * C32_MAX_BYTES;
+    char     *encoded   = (char*)CALLOCATE(amount, sizeof(char));
+    char     *target    = encoded;
+    char     *limit     = target + amount;
+    const char *decode;
+
+    for (set_num = 0; set_num < num_sets; set_num++) {
+        const char *skip;
+        ints = TestUtils_random_u64s(ints, count,
+                                     mins[set_num], limits[set_num]);
+        target = encoded;
+        for (size_t i = 0; i < count; i++) {
+            NumUtil_encode_c32((uint32_t)ints[i], &target);
+        }
+        decode = encoded;
+        skip   = encoded;
+        for (size_t i = 0; i < count; i++) {
+            TEST_INT_EQ(runner, NumUtil_decode_c32(&decode), (long)ints[i],
+                        "c32 %lu", (long)ints[i]);
+            NumUtil_skip_cint(&skip);
+            if (decode > limit) { THROW(ERR, "overrun"); }
+        }
+        TEST_TRUE(runner, skip == decode, "skip %lu == %lu",
+                  (unsigned long)skip, (unsigned long)decode);
+
+        target = encoded;
+        for (size_t i = 0; i < count; i++) {
+            NumUtil_encode_padded_c32((uint32_t)ints[i], &target);
+        }
+        TEST_TRUE(runner, target == limit,
+                  "padded c32 uses 5 bytes (%lu == %lu)", (unsigned long)target,
+                  (unsigned long)limit);
+        decode = encoded;
+        skip   = encoded;
+        for (size_t i = 0; i < count; i++) {
+            TEST_INT_EQ(runner, NumUtil_decode_c32(&decode), (long)ints[i],
+                        "padded c32 %lu", (long)ints[i]);
+            NumUtil_skip_cint(&skip);
+            if (decode > limit) { THROW(ERR, "overrun"); }
+        }
+        TEST_TRUE(runner, skip == decode, "skip padded %lu == %lu",
+                  (unsigned long)skip, (unsigned long)decode);
+    }
+
+    target = encoded;
+    NumUtil_encode_c32(UINT32_MAX, &target);
+    decode = encoded;
+    TEST_INT_EQ(runner, NumUtil_decode_c32(&decode), UINT32_MAX, "c32 UINT32_MAX");
+
+    FREEMEM(encoded);
+    FREEMEM(ints);
+}
+
+static void
+test_c64(TestBatchRunner *runner) {
+    uint64_t  mins[]    = { 0,   0x4000 - 100, (uint64_t)UINT32_MAX - 100,  UINT64_MAX - 10 };
+    uint64_t  limits[]  = { 500, 0x4000 + 100, (uint64_t)UINT32_MAX + 1000, UINT64_MAX      };
+    uint32_t  set_num;
+    uint32_t  num_sets  = sizeof(mins) / sizeof(uint64_t);
+    size_t    count     = 64;
+    uint64_t *ints      = NULL;
+    size_t    amount    = count * C64_MAX_BYTES;
+    char     *encoded   = (char*)CALLOCATE(amount, sizeof(char));
+    char     *target    = encoded;
+    char     *limit     = target + amount;
+    const char *decode;
+
+    for (set_num = 0; set_num < num_sets; set_num++) {
+        const char *skip;
+        ints = TestUtils_random_u64s(ints, count,
+                                     mins[set_num], limits[set_num]);
+        target = encoded;
+        for (size_t i = 0; i < count; i++) {
+            NumUtil_encode_c64(ints[i], &target);
+        }
+        decode = encoded;
+        skip   = encoded;
+        for (size_t i = 0; i < count; i++) {
+            uint64_t got = NumUtil_decode_c64(&decode);
+            TEST_TRUE(runner, got == ints[i],
+                      "c64 %" PRIu64 " == %" PRIu64, got, ints[i]);
+            if (decode > limit) { THROW(ERR, "overrun"); }
+            NumUtil_skip_cint(&skip);
+        }
+        TEST_TRUE(runner, skip == decode, "skip %lu == %lu",
+                  (unsigned long)skip, (unsigned long)decode);
+    }
+
+    target = encoded;
+    NumUtil_encode_c64(UINT64_MAX, &target);
+
+    decode = encoded;
+    uint64_t got = NumUtil_decode_c64(&decode);
+    TEST_TRUE(runner, got == UINT64_MAX, "c64 UINT64_MAX");
+
+    FREEMEM(encoded);
+    FREEMEM(ints);
+}
+
+static void
+test_bigend_u16(TestBatchRunner *runner) {
+    size_t    count     = 32;
+    uint64_t *ints      = TestUtils_random_u64s(NULL, count, 0, UINT16_MAX + 1);
+    size_t    amount    = (count + 1) * sizeof(uint16_t);
+    char     *allocated = (char*)CALLOCATE(amount, sizeof(char));
+    char     *encoded   = allocated + 1; // Intentionally misaligned.
+    char     *target    = encoded;
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_encode_bigend_u16((uint16_t)ints[i], &target);
+        target += sizeof(uint16_t);
+    }
+    target = encoded;
+    for (size_t i = 0; i < count; i++) {
+        uint16_t got = NumUtil_decode_bigend_u16(target);
+        TEST_INT_EQ(runner, got, (long)ints[i], "bigend u16");
+        target += sizeof(uint16_t);
+    }
+
+    target = encoded;
+    NumUtil_encode_bigend_u16(1, &target);
+    TEST_INT_EQ(runner, encoded[0], 0, "Truly big-endian u16");
+    TEST_INT_EQ(runner, encoded[1], 1, "Truly big-endian u16");
+
+    FREEMEM(allocated);
+    FREEMEM(ints);
+}
+
+static void
+test_bigend_u32(TestBatchRunner *runner) {
+    size_t    count     = 32;
+    uint64_t *ints      = TestUtils_random_u64s(NULL, count, 0, UINT64_C(1) + UINT32_MAX);
+    size_t    amount    = (count + 1) * sizeof(uint32_t);
+    char     *allocated = (char*)CALLOCATE(amount, sizeof(char));
+    char     *encoded   = allocated + 1; // Intentionally misaligned.
+    char     *target    = encoded;
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_encode_bigend_u32((uint32_t)ints[i], &target);
+        target += sizeof(uint32_t);
+    }
+    target = encoded;
+    for (size_t i = 0; i < count; i++) {
+        uint32_t got = NumUtil_decode_bigend_u32(target);
+        TEST_INT_EQ(runner, got, (long)ints[i], "bigend u32");
+        target += sizeof(uint32_t);
+    }
+
+    target = encoded;
+    NumUtil_encode_bigend_u32(1, &target);
+    TEST_INT_EQ(runner, encoded[0], 0, "Truly big-endian u32");
+    TEST_INT_EQ(runner, encoded[3], 1, "Truly big-endian u32");
+
+    FREEMEM(allocated);
+    FREEMEM(ints);
+}
+
+static void
+test_bigend_u64(TestBatchRunner *runner) {
+    size_t    count     = 32;
+    uint64_t *ints      = TestUtils_random_u64s(NULL, count, 0, UINT64_MAX);
+    size_t    amount    = (count + 1) * sizeof(uint64_t);
+    char     *allocated = (char*)CALLOCATE(amount, sizeof(char));
+    char     *encoded   = allocated + 1; // Intentionally misaligned.
+    char     *target    = encoded;
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_encode_bigend_u64(ints[i], &target);
+        target += sizeof(uint64_t);
+    }
+    target = encoded;
+    for (size_t i = 0; i < count; i++) {
+        uint64_t got = NumUtil_decode_bigend_u64(target);
+        TEST_TRUE(runner, got == ints[i], "bigend u64");
+        target += sizeof(uint64_t);
+    }
+
+    target = encoded;
+    NumUtil_encode_bigend_u64(1, &target);
+    TEST_INT_EQ(runner, encoded[0], 0, "Truly big-endian");
+    TEST_INT_EQ(runner, encoded[7], 1, "Truly big-endian");
+
+    FREEMEM(allocated);
+    FREEMEM(ints);
+}
+
+static void
+test_bigend_f32(TestBatchRunner *runner) {
+    float    source[]  = { -1.3f, 0.0f, 100.2f };
+    size_t   count     = 3;
+    size_t   amount    = (count + 1) * sizeof(float);
+    uint8_t *allocated = (uint8_t*)CALLOCATE(amount, sizeof(uint8_t));
+    uint8_t *encoded   = allocated + 1; // Intentionally misaligned.
+    uint8_t *target    = encoded;
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_encode_bigend_f32(source[i], &target);
+        target += sizeof(float);
+    }
+    target = encoded;
+    for (size_t i = 0; i < count; i++) {
+        float got = NumUtil_decode_bigend_f32(target);
+        TEST_TRUE(runner, got == source[i], "bigend f32");
+        target += sizeof(float);
+    }
+
+    target = encoded;
+    NumUtil_encode_bigend_f32(-2.0f, &target);
+    TEST_INT_EQ(runner, (encoded[0] & 0x80), 0x80,
+                "Truly big-endian (IEEE 754 sign bit set for negative number)");
+    TEST_INT_EQ(runner, encoded[0], 0xC0,
+                "IEEE 754 representation of -2.0f, byte 0");
+    for (size_t i = 1; i < sizeof(float); i++) {
+        TEST_INT_EQ(runner, encoded[i], 0,
+                    "IEEE 754 representation of -2.0f, byte %d", (int)i);
+    }
+
+    FREEMEM(allocated);
+}
+
+static void
+test_bigend_f64(TestBatchRunner *runner) {
+    double   source[]  = { -1.3, 0.0, 100.2 };
+    size_t   count     = 3;
+    size_t   amount    = (count + 1) * sizeof(double);
+    uint8_t *allocated = (uint8_t*)CALLOCATE(amount, sizeof(uint8_t));
+    uint8_t *encoded   = allocated + 1; // Intentionally misaligned.
+    uint8_t *target    = encoded;
+
+    for (size_t i = 0; i < count; i++) {
+        NumUtil_encode_bigend_f64(source[i], &target);
+        target += sizeof(double);
+    }
+    target = encoded;
+    for (size_t i = 0; i < count; i++) {
+        double got = NumUtil_decode_bigend_f64(target);
+        TEST_TRUE(runner, got == source[i], "bigend f64");
+        target += sizeof(double);
+    }
+
+    target = encoded;
+    NumUtil_encode_bigend_f64(-2.0, &target);
+    TEST_INT_EQ(runner, (encoded[0] & 0x80), 0x80,
+                "Truly big-endian (IEEE 754 sign bit set for negative number)");
+    TEST_INT_EQ(runner, encoded[0], 0xC0,
+                "IEEE 754 representation of -2.0, byte 0");
+    for (size_t i = 1; i < sizeof(double); i++) {
+        TEST_INT_EQ(runner, encoded[i], 0,
+                    "IEEE 754 representation of -2.0, byte %d", (int)i);
+    }
+
+    FREEMEM(allocated);
+}
+
+void
+TestNumUtil_Run_IMP(TestNumberUtils *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 1196);
+    srand((unsigned int)time((time_t*)NULL));
+    test_u1(runner);
+    test_u2(runner);
+    test_u4(runner);
+    test_c32(runner);
+    test_c64(runner);
+    test_bigend_u16(runner);
+    test_bigend_u32(runner);
+    test_bigend_u64(runner);
+    test_bigend_f32(runner);
+    test_bigend_f64(runner);
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Test/Util/TestNumberUtils.cfh
----------------------------------------------------------------------
diff --git a/core/Lucy/Test/Util/TestNumberUtils.cfh b/core/Lucy/Test/Util/TestNumberUtils.cfh
new file mode 100644
index 0000000..fcb20a9
--- /dev/null
+++ b/core/Lucy/Test/Util/TestNumberUtils.cfh
@@ -0,0 +1,29 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestLucy;
+
+class Lucy::Test::Util::TestNumberUtils nickname TestNumUtil
+    inherits Clownfish::TestHarness::TestBatch {
+
+    inert incremented TestNumberUtils*
+    new();
+
+    void
+    Run(TestNumberUtils *self, TestBatchRunner *runner);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Util/NumberUtils.cfh
----------------------------------------------------------------------
diff --git a/core/Lucy/Util/NumberUtils.cfh b/core/Lucy/Util/NumberUtils.cfh
new file mode 100644
index 0000000..b7c08b4
--- /dev/null
+++ b/core/Lucy/Util/NumberUtils.cfh
@@ -0,0 +1,466 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel Lucy;
+
+/** Provide various number-related utilies.
+ *
+ * Provide utilities for dealing with endian issues, sub-byte-width arrays,
+ * compressed integers, and so on.
+ */
+inert class Lucy::Util::NumberUtils nickname NumUtil {
+
+    /** Encode an unsigned 16-bit integer as 2 bytes in the buffer provided,
+     * using big-endian byte order.
+     */
+    inert inline void
+    encode_bigend_u16(uint16_t value, void *dest);
+
+    /** Encode an unsigned 32-bit integer as 4 bytes in the buffer provided,
+     * using big-endian byte order.
+     */
+    inert inline void
+    encode_bigend_u32(uint32_t value, void *dest);
+
+    /** Encode an unsigned 64-bit integer as 8 bytes in the buffer provided,
+     * using big-endian byte order.
+     */
+    inert inline void
+    encode_bigend_u64(uint64_t value, void *dest);
+
+    /** Interpret a sequence of bytes as a big-endian unsigned 16-bit int.
+     */
+    inert inline uint16_t
+    decode_bigend_u16(const void *source);
+
+    /** Interpret a sequence of bytes as a big-endian unsigned 32-bit int.
+     */
+    inert inline uint32_t
+    decode_bigend_u32(const void *source);
+
+    /** Interpret a sequence of bytes as a big-endian unsigned 64-bit int.
+     */
+    inert inline uint64_t
+    decode_bigend_u64(const void *source);
+
+    /** Encode a 32-bit floating point number as 4 bytes in the buffer
+     * provided, using big-endian byte order.
+     */
+    inert inline void
+    encode_bigend_f32(float value, void *dest);
+
+    /** Encode a 64-bit floating point number as 8 bytes in the buffer
+     * provided, using big-endian byte order.
+     */
+    inert inline void
+    encode_bigend_f64(double value, void *dest);
+
+    /** Interpret a sequence of bytes as a 32-bit float stored in big-endian
+     * byte order.
+     */
+    inert inline float
+    decode_bigend_f32(const void *source);
+
+    /** Interpret a sequence of bytes as a 64-bit float stored in big-endian
+     * byte order.
+     */
+    inert inline double
+    decode_bigend_f64(const void *source);
+
+    /** Encode a C32 at the space pointed to by `dest`. As a side
+     * effect, `dest` will be advanced to immediately after the end
+     * of the C32.
+     */
+    inert inline void
+    encode_c32(uint32_t value, char **dest);
+
+    /** Encode a C32 at the space pointed to by `dest`, but add
+     * "leading zeroes" so that the space consumed will always be 5 bytes.  As
+     * a side effect, `dest` will be advanced to immediately after
+     * the end of the C32.
+     */
+    inert inline void
+    encode_padded_c32(uint32_t value, char **dest);
+
+    /** Encode a C64 at the space pointed to by `dest`. As a side
+     * effect, `dest` will be advanced to immediately after the end
+     * of the C64.
+     */
+    inert inline void
+    encode_c64(uint64_t value, char **dest);
+
+    /** Read a C32 from the buffer pointed to by `source`.  As a
+     * side effect, advance the pointer, consuming the bytes occupied by the
+     * C32.
+     */
+    inert inline uint32_t
+    decode_c32(const char **source);
+
+    /** Read a C64 from the buffer pointed to by `source`.  As a
+     * side effect, advance the pointer, consuming the bytes occupied by the
+     * C64.
+     */
+    inert inline uint64_t
+    decode_c64(const char **source);
+
+    /** Advance `source` past one encoded C32 or C64.
+     */
+    inert inline void
+    skip_cint(const char **source);
+
+    /** Interpret `array` as an array of bits; return true if the
+     * bit at `tick` is set, false otherwise.
+     */
+    inert inline bool
+    u1get(const void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of bits; set the bit at
+     * `tick`.
+     */
+    inert inline void
+    u1set(void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of bits; clear the bit at
+     * `tick`.
+     */
+    inert inline void
+    u1clear(void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of bits; flip the bit at
+     * `tick`.
+     */
+    inert inline void
+    u1flip(void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of two-bit integers; return
+     * the value at `tick`.
+     */
+    inert inline uint8_t
+    u2get(const void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of two-bit integers; set the
+     * element at `tick` to `value`.
+     */
+    inert inline void
+    u2set(void *array, uint32_t tick, uint8_t value);
+
+    /** Interpret `array` as an array of four-bit integers; return
+     * the value at `tick`.
+     */
+    inert inline uint8_t
+    u4get(const void *array, uint32_t tick);
+
+    /** Interpret `array` as an array of four-bit integers; set the
+     * element at `tick` to `value`.
+     */
+    inert inline void
+    u4set(void *array, uint32_t tick, uint8_t value);
+}
+
+__C__
+
+#include <string.h>
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_bigend_u16(uint16_t value, void *dest_ptr) {
+    uint8_t *dest = *(uint8_t**)dest_ptr;
+#ifdef CFISH_BIG_END
+    memcpy(dest, &value, sizeof(uint16_t));
+#else /* little endian */
+    uint8_t *source = (uint8_t*)&value;
+    dest[0] = source[1];
+    dest[1] = source[0];
+#endif /* CFISH_BIG_END (and little endian) */
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_bigend_u32(uint32_t value, void *dest_ptr) {
+    uint8_t *dest = *(uint8_t**)dest_ptr;
+#ifdef CFISH_BIG_END
+    memcpy(dest, &value, sizeof(uint32_t));
+#else /* little endian */
+    uint8_t *source = (uint8_t*)&value;
+    dest[0] = source[3];
+    dest[1] = source[2];
+    dest[2] = source[1];
+    dest[3] = source[0];
+#endif /* CFISH_BIG_END (and little endian) */
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_bigend_u64(uint64_t value, void *dest_ptr) {
+    uint8_t *dest = *(uint8_t**)dest_ptr;
+#ifdef CFISH_BIG_END
+    memcpy(dest, &value, sizeof(uint64_t));
+#else /* little endian */
+    uint8_t *source = (uint8_t*)&value;
+    dest[0] = source[7];
+    dest[1] = source[6];
+    dest[2] = source[5];
+    dest[3] = source[4];
+    dest[4] = source[3];
+    dest[5] = source[2];
+    dest[6] = source[1];
+    dest[7] = source[0];
+#endif /* CFISH_BIG_END (and little endian) */
+}
+
+static CFISH_INLINE uint16_t
+lucy_NumUtil_decode_bigend_u16(const void *source) {
+    const uint8_t *const buf = (const uint8_t*)source;
+    return  ((uint16_t)buf[0] << 8) |
+            ((uint16_t)buf[1]);
+}
+
+static CFISH_INLINE uint32_t
+lucy_NumUtil_decode_bigend_u32(const void *source) {
+    const uint8_t *const buf = (const uint8_t*)source;
+    return  ((uint32_t)buf[0]  << 24) |
+            ((uint32_t)buf[1]  << 16) |
+            ((uint32_t)buf[2]  << 8)  |
+            ((uint32_t)buf[3]);
+}
+
+static CFISH_INLINE uint64_t
+lucy_NumUtil_decode_bigend_u64(const void *source) {
+    const uint8_t *const buf = (const uint8_t*)source;
+    uint64_t high_bits = ((uint32_t)buf[0]  << 24) |
+                         ((uint32_t)buf[1]  << 16) |
+                         ((uint32_t)buf[2]  << 8)  |
+                         ((uint32_t)buf[3]);
+    uint32_t low_bits  = ((uint32_t)buf[4]  << 24) |
+                         ((uint32_t)buf[5]  << 16) |
+                         ((uint32_t)buf[6]  << 8)  |
+                         ((uint32_t)buf[7]);
+    uint64_t retval = high_bits << 32;
+    retval |= low_bits;
+    return retval;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_bigend_f32(float value, void *dest_ptr) {
+    uint8_t *dest = *(uint8_t**)dest_ptr;
+#ifdef CFISH_BIG_END
+    memcpy(dest, &value, sizeof(float));
+#else
+    union { float f; uint32_t u32; } duo;
+    duo.f = value;
+    lucy_NumUtil_encode_bigend_u32(duo.u32, &dest);
+#endif
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_bigend_f64(double value, void *dest_ptr) {
+    uint8_t *dest = *(uint8_t**)dest_ptr;
+#ifdef CFISH_BIG_END
+    memcpy(dest, &value, sizeof(double));
+#else
+    union { double d; uint64_t u64; } duo;
+    duo.d = value;
+    lucy_NumUtil_encode_bigend_u64(duo.u64, &dest);
+#endif
+}
+
+static CFISH_INLINE float
+lucy_NumUtil_decode_bigend_f32(const void *source) {
+    union { float f; uint32_t u32; } duo;
+    memcpy(&duo, source, sizeof(float));
+#ifdef CFISH_LITTLE_END
+    duo.u32 = lucy_NumUtil_decode_bigend_u32(&duo.u32);
+#endif
+    return duo.f;
+}
+
+static CFISH_INLINE double
+lucy_NumUtil_decode_bigend_f64(const void *source) {
+    union { double d; uint64_t u64; } duo;
+    memcpy(&duo, source, sizeof(double));
+#ifdef CFISH_LITTLE_END
+    duo.u64 = lucy_NumUtil_decode_bigend_u64(&duo.u64);
+#endif
+    return duo.d;
+}
+
+#define LUCY_NUMUTIL_C32_MAX_BYTES ((sizeof(uint32_t) * 8 / 7) + 1) /*  5 */
+#define LUCY_NUMUTIL_C64_MAX_BYTES ((sizeof(uint64_t) * 8 / 7) + 1) /* 10 */
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_c32(uint32_t value, char **out_buf) {
+    uint8_t   buf[LUCY_NUMUTIL_C32_MAX_BYTES];
+    uint8_t  *const limit = buf + sizeof(buf);
+    uint8_t  *ptr         = limit - 1;
+    int       num_bytes;
+    /* Write last byte first, which has no continue bit. */
+    *ptr = value & 0x7f;
+    value >>= 7;
+    while (value) {
+        /* Work backwards, writing bytes with continue bits set. */
+        *--ptr = ((value & 0x7f) | 0x80);
+        value >>= 7;
+    }
+    num_bytes = limit - ptr;
+    memcpy(*out_buf, ptr, num_bytes);
+    *out_buf += num_bytes;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_c64(uint64_t value, char **out_buf) {
+    uint8_t   buf[LUCY_NUMUTIL_C64_MAX_BYTES];
+    uint8_t  *const limit = buf + sizeof(buf);
+    uint8_t  *ptr         = limit - 1;
+    int       num_bytes;
+    /* Write last byte first, which has no continue bit. */
+    *ptr = value & 0x7f;
+    value >>= 7;
+    while (value) {
+        /* Work backwards, writing bytes with continue bits set. */
+        *--ptr = ((value & 0x7f) | 0x80);
+        value >>= 7;
+    }
+    num_bytes = limit - ptr;
+    memcpy(*out_buf, ptr, num_bytes);
+    *out_buf += num_bytes;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_encode_padded_c32(uint32_t value, char **out_buf) {
+    uint8_t buf[LUCY_NUMUTIL_C32_MAX_BYTES]
+        = { 0x80, 0x80, 0x80, 0x80, 0x80 };
+    uint8_t *const limit = buf + sizeof(buf);
+    uint8_t *ptr         = limit - 1;
+    /* Write last byte first, which has no continue bit. */
+    *ptr = value & 0x7f;
+    value >>= 7;
+    while (value) {
+        /* Work backwards, writing bytes with continue bits set. */
+        *--ptr = ((value & 0x7f) | 0x80);
+        value >>= 7;
+    }
+    memcpy(*out_buf, buf, LUCY_NUMUTIL_C32_MAX_BYTES);
+    *out_buf += sizeof(buf);
+}
+
+/* Decode a compressed integer up to size of 'var', advancing 'source' */
+#define LUCY_NUMUTIL_DECODE_CINT(var, source) \
+    do { \
+        var = (*source & 0x7f); \
+        while (*source++ & 0x80) { \
+            var = (*source & 0x7f) | (var << 7); \
+        }  \
+    } while (0)
+
+static CFISH_INLINE uint32_t
+lucy_NumUtil_decode_c32(const char **source_ptr) {
+    const char *source = *source_ptr;
+    uint32_t decoded;
+    LUCY_NUMUTIL_DECODE_CINT(decoded, source);
+    *source_ptr = source;
+    return decoded;
+}
+
+static CFISH_INLINE uint64_t
+lucy_NumUtil_decode_c64(const char **source_ptr) {
+    const char *source = *source_ptr;
+    uint64_t decoded;
+    LUCY_NUMUTIL_DECODE_CINT(decoded, source);
+    *source_ptr = source;
+    return decoded;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_skip_cint(const char **source_ptr) {
+    const uint8_t *ptr = *(const uint8_t**)source_ptr;
+    while ((*ptr++ & 0x80) != 0) { }
+    *source_ptr = (const char*)ptr;
+}
+
+static CFISH_INLINE bool
+lucy_NumUtil_u1get(const void *array, uint32_t tick) {
+    uint8_t *const u8bits      = (uint8_t*)array;
+    const uint32_t byte_offset = tick >> 3;
+    const uint8_t  mask        = 1 << (tick & 0x7);
+    return !((u8bits[byte_offset] & mask) == 0);
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_u1set(void *array, uint32_t tick) {
+    uint8_t *const u8bits      = (uint8_t*)array;
+    const uint32_t byte_offset = tick >> 3;
+    const uint8_t  mask        = 1 << (tick & 0x7);
+    u8bits[byte_offset] |= mask;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_u1clear(void *array, uint32_t tick) {
+    uint8_t *const u8bits      = (uint8_t*)array;
+    const uint32_t byte_offset = tick >> 3;
+    const uint8_t  mask        = 1 << (tick & 0x7);
+    u8bits[byte_offset] &= ~mask;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_u1flip(void *array, uint32_t tick) {
+    uint8_t *const u8bits      = (uint8_t*)array;
+    const uint32_t byte_offset = tick >> 3;
+    const uint8_t  mask        = 1 << (tick & 0x7);
+    u8bits[byte_offset] ^= mask;
+}
+
+static CFISH_INLINE uint8_t
+lucy_NumUtil_u2get(const void *array, uint32_t tick) {
+    uint8_t *ints  = (uint8_t*)array;
+    uint8_t  byte  = ints[(tick >> 2)];
+    int      shift = 2 * (tick & 0x3);
+    return (byte >> shift) & 0x3;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_u2set(void *array, uint32_t tick, uint8_t value) {
+    uint8_t *ints     = (uint8_t*)array;
+    int      shift    = 2 * (tick & 0x3);
+    uint8_t  mask     = 0x3 << shift;
+    uint8_t  new_val  = value & 0x3;
+    uint8_t  new_bits = new_val << shift;
+    ints[(tick >> 2)]  = (ints[(tick >> 2)] & ~mask) | new_bits;
+}
+
+
+static CFISH_INLINE uint8_t
+lucy_NumUtil_u4get(const void *array, uint32_t tick) {
+    uint8_t *ints  = (uint8_t*)array;
+    uint8_t  byte  = ints[(tick >> 1)];
+    int      shift = 4 * (tick & 1);
+    return (byte >> shift) & 0xF;
+}
+
+static CFISH_INLINE void
+lucy_NumUtil_u4set(void *array, uint32_t tick, uint8_t value) {
+    uint8_t  *ints     = (uint8_t*)array;
+    int       shift    = 4 * (tick & 0x1);
+    uint8_t   mask     = 0xF << shift;
+    uint8_t   new_val  = value & 0xF;
+    uint8_t   new_bits = new_val << shift;
+    ints[(tick >> 1)]  = (ints[(tick >> 1)] & ~mask) | new_bits;
+}
+
+#ifdef LUCY_USE_SHORT_NAMES
+  #define C32_MAX_BYTES                LUCY_NUMUTIL_C32_MAX_BYTES
+  #define C64_MAX_BYTES                LUCY_NUMUTIL_C64_MAX_BYTES
+#endif
+
+__END_C__
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/core/Lucy/Util/ToolSet.h
----------------------------------------------------------------------
diff --git a/core/Lucy/Util/ToolSet.h b/core/Lucy/Util/ToolSet.h
index 853d51a..ee1b810 100644
--- a/core/Lucy/Util/ToolSet.h
+++ b/core/Lucy/Util/ToolSet.h
@@ -45,7 +45,6 @@ extern "C" {
 #include "Clownfish/Num.h"
 #include "Clownfish/Vector.h"
 #include "Clownfish/Class.h"
-#include "Clownfish/Util/NumberUtils.h"
 #include "Clownfish/Util/Memory.h"
 #include "Clownfish/Util/StringHelper.h"
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/8782bd56/perl/t/core/030-number_utils.t
----------------------------------------------------------------------
diff --git a/perl/t/core/030-number_utils.t b/perl/t/core/030-number_utils.t
new file mode 100644
index 0000000..2960c4e
--- /dev/null
+++ b/perl/t/core/030-number_utils.t
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+
+use Lucy::Test;
+my $success = Lucy::Test::run_tests(
+    "Lucy::Test::Util::TestNumberUtils"
+);
+
+exit($success ? 0 : 1);
+