Merge pull request #7057 from theamirocohen/trng_test

Add TRNG API test
pull/7970/merge
Martin Kojtal 2018-09-26 09:48:36 +02:00 committed by GitHub
commit a0a9b54e97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1479 additions and 0 deletions

View File

@ -0,0 +1,145 @@
"""
Copyright (c) 2018 ARM Limited
Licensed 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.
"""
"""
This script is the host script for trng test sequence, it send the
step signaling sequence and receive and transmit data to the device after
reset if necesarry (default loading and storing mechanism while reseting the device
is NVstore, in case NVstore isn't enabled we'll use current infrastructure,
for more details see main.cpp file)
"""
import time
from mbed_host_tests import BaseHostTest
from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector
from time import sleep
DEFAULT_CYCLE_PERIOD = 1.0
MSG_VALUE_DUMMY = '0'
MSG_TRNG_READY = 'ready'
MSG_TRNG_BUFFER = 'buffer'
MSG_TRNG_TEST_STEP1 = 'check_step1'
MSG_TRNG_TEST_STEP2 = 'check_step2'
MSG_KEY_SYNC = '__sync'
MSG_KEY_RESET_COMPLETE = 'reset_complete'
MSG_KEY_TEST_SUITE_ENDED = 'Test_suite_ended'
MSG_KEY_EXIT = 'exit'
class TRNGResetTest(BaseHostTest):
"""Test for the TRNG API.
"""
def __init__(self):
super(TRNGResetTest, self).__init__()
self.did_reset = False
self.ready = False
self.suite_ended = False
self.buffer = 0
self.test_steps_sequence = self.test_steps()
# Advance the coroutine to it's first yield statement.
self.test_steps_sequence.send(None)
#define callback functions for msg handling
def setup(self):
self.register_callback(MSG_TRNG_READY, self.cb_device_ready)
self.register_callback(MSG_TRNG_BUFFER, self.cb_trng_buffer)
self.register_callback(MSG_KEY_TEST_SUITE_ENDED, self.cb_device_test_suit_ended)
self.register_callback(MSG_KEY_RESET_COMPLETE, self.cb_reset_complete)
#receive sent data from device before reset
def cb_trng_buffer(self, key, value, timestamp):
"""Acknowledge device rebooted correctly and feed the test execution
"""
self.buffer = value
try:
if self.test_steps_sequence.send(value):
self.notify_complete(True)
except (StopIteration, RuntimeError) as exc:
self.notify_complete(False)
def cb_device_ready(self, key, value, timestamp):
"""Acknowledge device rebooted correctly and feed the test execution
"""
self.ready = True
try:
if self.test_steps_sequence.send(value):
self.notify_complete(True)
except (StopIteration, RuntimeError) as exc:
self.notify_complete(False)
def cb_reset_complete(self, key, value, timestamp):
"""Acknowledge reset complete
"""
self.did_reset = True
try:
if self.test_steps_sequence.send(value):
self.notify_complete(True)
except (StopIteration, RuntimeError) as exc:
self.notify_complete(False)
def cb_device_test_suit_ended(self, key, value, timestamp):
"""Acknowledge device finished a test step correctly and feed the test execution
"""
self.suite_ended = True
try:
if self.test_steps_sequence.send(value):
self.notify_complete(True)
except (StopIteration, RuntimeError) as exc:
self.notify_complete(False)
#define test steps and actions
def test_steps(self):
"""Test step 1
"""
wait_for_communication = yield
self.ready = False
self.did_reset = False
self.suite_ended = False
self.send_kv(MSG_TRNG_TEST_STEP1, MSG_VALUE_DUMMY)
wait_for_communication = yield
if self.buffer == 0:
raise RuntimeError('Phase 1: No buffer received.')
self.reset()
"""Test step 2 (After reset)
"""
wait_for_communication = yield
if self.did_reset == False:
raise RuntimeError('Phase 1: Platform did not reset as expected.')
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
wait_for_communication = yield
if self.ready == False:
raise RuntimeError('Phase 1: Platform not ready as expected.')
self.send_kv(MSG_TRNG_TEST_STEP2, self.buffer)
wait_for_communication = yield
if self.suite_ended == False:
raise RuntimeError('Test failed.')
self.send_kv(MSG_KEY_EXIT, MSG_VALUE_DUMMY)
# The sequence is correct -- test passed.
yield

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed 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 "base64b.h"
using namespace std;
static char IntToBase64Char(uint8_t intVal)
{
const char *base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
return base64Digits[intVal & 0x3F];
}
#define BASE_64_PAD 0xFF
static base64_result_e Base64CharToInt(char base64, uint8_t *intVal)
{
if (NULL == intVal) {
return BASE64_INVALID_PARAMETER;
}
if ((base64 >= 'A') && (base64 <= 'Z')) {
*intVal = base64 - 'A' ;
} else if ((base64 >= 'a') && (base64 <= 'z')) {
*intVal = base64 - 'a' + 26;
} else if ((base64 >= '0') && (base64 <= '9')) {
*intVal = base64 - '0' + 52;
} else if (base64 == '+') {
*intVal = 62;
} else if (base64 == '/') {
*intVal = 63;
} else if (base64 == '=') {
*intVal = BASE_64_PAD;
} else {
return BASE64_ERROR;
}
return BASE64_SUCCESS;
}
base64_result_e trng_DecodeNBase64(const char *string,
uint32_t stringMaxSize,
void *buffer,
uint32_t bufferSize,
uint32_t *lengthWritten,
uint32_t *charsProcessed)
{
base64_result_e result = BASE64_SUCCESS;
uint32_t bitOffset = 0;
uint8_t *writePtr = (uint8_t *)buffer;
uint8_t *bufferEnd = (uint8_t *)buffer + bufferSize;
uint8_t tempVal = 0;
uint32_t currPos = 0;
uint32_t localBytesWritten = 0;
uint32_t localCharsProcessed = 0;
bool isEndOfString = false;
if ((NULL == string) || (NULL == buffer) || (bufferSize == 0)) {
return BASE64_INVALID_PARAMETER;
}
*writePtr = 0;
while (( currPos < stringMaxSize ) &&
( string[currPos] != 0 ) &&
( writePtr < bufferEnd ) &&
( !isEndOfString )) {
uint8_t val;
if (string[currPos] == 0) {
break;
}
result = Base64CharToInt(string[currPos++], &val);
if (result != BASE64_SUCCESS) {
break;
}
if (val != BASE_64_PAD) {
if (bitOffset <= 2) {
tempVal |= val << (2 - bitOffset);
if (bitOffset == 2) {
*writePtr++ = tempVal;
tempVal = 0;
}
} else {
*writePtr++ = (uint8_t)(tempVal | (val >> (bitOffset - 2)));
tempVal = (uint8_t)(val << (10 - bitOffset));
}
} else { // found BASE_64_PAD
// At most two pad characters may occur at the end of the encoded stream
if (bitOffset == 2) {
isEndOfString = true; // The last padding byte has been processed.
} else if (bitOffset != 4) {
return BASE64_ERROR; // Incorrect padding
}
}
bitOffset = (bitOffset + 6) & 0x7;
if (bitOffset == 0) {
localBytesWritten = (uint32_t)(writePtr - (uint8_t *)buffer);
localCharsProcessed = currPos;
}
}
if (charsProcessed == NULL) {
localBytesWritten = (uint32_t)(writePtr - (uint8_t *)buffer);
} else {
*charsProcessed = localCharsProcessed;
}
if (lengthWritten != NULL) {
*lengthWritten = localBytesWritten;
} else if (bufferSize != localBytesWritten) {
return BASE64_BUFFER_TOO_SMALL;
}
// Check if additional bytes should have been processed but buffer isn't sufficient.
if (( result == BASE64_SUCCESS ) &&
( !isEndOfString ) &&
( currPos < stringMaxSize ) &&
( string[currPos] != 0 ) &&
( string[currPos] != '=' ) ) {
return BASE64_BUFFER_TOO_SMALL;
}
if (result != BASE64_SUCCESS) {
return result;
}
return BASE64_SUCCESS;
}
base64_result_e trng_EncodeBase64(const void *buffer, uint32_t bufferSize, char *string, uint32_t stringSize)
{
uint32_t bitOffset = 0;
const uint8_t *readPtr = (const uint8_t *)buffer;
const uint8_t *bufferEnd = (const uint8_t *)buffer + bufferSize;
char *writePtr = string;
char *stringEnd = string + stringSize - 1;
if ((NULL == string) || (NULL == buffer) || (stringSize == 0)) {
return BASE64_INVALID_PARAMETER;
}
stringSize--;
while (readPtr < bufferEnd && writePtr < stringEnd) {
uint8_t tempVal = 0;
switch (bitOffset) {
case 0:
*writePtr++ = IntToBase64Char(*readPtr >> 2); // take upper 6 bits
break;
case 6:
tempVal = *readPtr++ << 4;
if (readPtr < bufferEnd) {
tempVal |= *readPtr >> 4;
}
*writePtr++ = IntToBase64Char(tempVal);
break;
case 4:
tempVal = *readPtr++ << 2;
if (readPtr < bufferEnd) {
tempVal |= *readPtr >> 6;
}
*writePtr++ = IntToBase64Char(tempVal);
break;
case 2:
*writePtr++ = IntToBase64Char(*readPtr++);
break;
default:
return BASE64_ERROR; // we should never reach this code.
}
bitOffset = (bitOffset + 6) & 0x7;
}
while (bitOffset > 0 && writePtr < stringEnd) {
*writePtr++ = '=';
bitOffset = (bitOffset + 6) & 0x7;
}
*writePtr = 0;
if ((readPtr < bufferEnd) || (bitOffset != 0)) {
return (BASE64_BUFFER_TOO_SMALL);
}
return (BASE64_SUCCESS);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed 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 <stdint.h>
#include <stdlib.h>
#include <string>
typedef enum {
BASE64_SUCCESS = 0,
BASE64_INVALID_PARAMETER = 1,
BASE64_BUFFER_TOO_SMALL = 2,
BASE64_ERROR = 3,
} base64_result_e;
base64_result_e trng_EncodeBase64(const void *buffer, uint32_t bufferSize, char *string, uint32_t stringSize);
base64_result_e trng_DecodeNBase64(const char *string, uint32_t stringMaxSize, void *buffer, uint32_t bufferSize,
uint32_t *lengthWritten, uint32_t *charsProcessed);

View File

@ -0,0 +1,247 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed 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.
*/
/*
* The test is based on the assumption that trng will generate random data, random so
* there will not be any similar patterns in it, that kind of data will be impossible to
* compress, if compression will occur the test will result in failure.
*
* The test is composed out of three parts:
* the first, generate a trng buffer and try to compress it, at the end of first part
* we will reset the device.
* In second part we will generate a trng buffer with a different buffer size and try to
* compress it.
* In the third part we will again generate a trng buffer to see that the same trng output
* is not generated as the stored trng buffer from part one (before reseting), the new trng data will
* be concatenated to the trng data from the first part and then try to compress it
* together, if there are similar patterns the compression will succeed.
*
* We need to store and load the first part data before and after reset, the mechanism
* we will use is the mbed greentea platform for sending and receving the data from the device
* to the host running the test and back, the problem with this mechanism is that it doesn't
* handle well certain characters, especially non ASCII ones, so we use the base64 algorithm
* to ensure all characters will be transmitted correctly.
*/
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest/utest.h"
#include "hal/trng_api.h"
#include "base64b.h"
#include "pithy.h"
#include <stdio.h>
#if !DEVICE_TRNG
#error [NOT_SUPPORTED] TRNG API not supported for this target
#endif
#define MSG_VALUE_DUMMY "0"
#define MSG_VALUE_LEN 64
#define MSG_KEY_LEN 32
#define BUFFER_LEN (MSG_VALUE_LEN/2)
#define MSG_TRNG_READY "ready"
#define MSG_TRNG_BUFFER "buffer"
#define MSG_TRNG_EXIT "exit"
#define MSG_TRNG_TEST_STEP1 "check_step1"
#define MSG_TRNG_TEST_STEP2 "check_step2"
#define MSG_TRNG_TEST_SUITE_ENDED "Test_suite_ended"
#define RESULT_SUCCESS 0
using namespace utest::v1;
static int fill_buffer_trng(uint8_t *buffer, trng_t *trng_obj, size_t trng_len)
{
size_t temp_size = 0, output_length = 0;
int trng_res = 0;
uint8_t *temp_in_buf = buffer;
trng_init(trng_obj);
memset(buffer, 0, BUFFER_LEN);
while (true) {
trng_res = trng_get_bytes(trng_obj, temp_in_buf, trng_len - temp_size, &output_length);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, trng_res, "trng_get_bytes error!");
temp_size += output_length;
temp_in_buf += output_length;
if (temp_size >= trng_len) {
break;
}
}
temp_in_buf = NULL;
trng_free(trng_obj);
return 0;
}
void print_array(uint8_t *buffer, size_t size)
{
for (size_t i=0; i < size; i++) {
utest_printf("%02x", buffer[i]);
}
utest_printf("\n");
}
static void compress_and_compare(char *key, char *value)
{
trng_t trng_obj;
uint8_t *out_comp_buf, *buffer;
uint8_t *input_buf, *temp_buf;
size_t comp_sz = 0;
unsigned int result = 0;
#define OUT_COMP_BUF_SIZE ((BUFFER_LEN * 5) + 32)
#define TEMP_BUF_SIZE (BUFFER_LEN * 2)
out_comp_buf = new uint8_t[OUT_COMP_BUF_SIZE];
buffer = new uint8_t[BUFFER_LEN];
temp_buf = new uint8_t[BUFFER_LEN * 2];
input_buf = new uint8_t[BUFFER_LEN * 4];
/*At the begining of step 2 load trng buffer from step 1*/
if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0) {
/*Using base64 to decode data sent from host*/
uint32_t lengthWritten = 0;
uint32_t charsProcessed = 0;
result = trng_DecodeNBase64((const char *)value,
MSG_VALUE_LEN,
buffer,
BUFFER_LEN,
&lengthWritten,
&charsProcessed);
TEST_ASSERT_EQUAL(0, result);
memcpy(input_buf, buffer, BUFFER_LEN);
}
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0) {
/*Fill buffer with trng values*/
result = fill_buffer_trng(buffer, &trng_obj, BUFFER_LEN);
TEST_ASSERT_EQUAL(0, result);
memcpy(input_buf, buffer, BUFFER_LEN);
}
/*pithy_Compress will try to compress the random data, if it succeeded it means the data is not really random*/
else if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0) {
comp_sz = pithy_Compress((char *)buffer,
BUFFER_LEN,
(char *)out_comp_buf,
OUT_COMP_BUF_SIZE,
9);
if (comp_sz <= BUFFER_LEN){
print_array(buffer, BUFFER_LEN);
}
TEST_ASSERT_MESSAGE(comp_sz > BUFFER_LEN,
"TRNG_TEST_STEP1: trng_get_bytes was able to compress thus not random");
/*pithy_Compress will try to compress the random data with a different buffer size*/
result = fill_buffer_trng(temp_buf, &trng_obj, TEMP_BUF_SIZE);
TEST_ASSERT_EQUAL(0, result);
comp_sz = pithy_Compress((char *)temp_buf,
TEMP_BUF_SIZE,
(char *)out_comp_buf,
OUT_COMP_BUF_SIZE,
9);
if (comp_sz <= TEMP_BUF_SIZE){
print_array(temp_buf, TEMP_BUF_SIZE);
}
TEST_ASSERT_MESSAGE(comp_sz > TEMP_BUF_SIZE,
"TRNG_TEST_STEP2: trng_get_bytes was able to compress thus not random");
memcpy(input_buf + BUFFER_LEN, temp_buf, TEMP_BUF_SIZE);
/*pithy_Compress will try to compress the random data from before reset concatenated with new random data*/
comp_sz = pithy_Compress((char *)input_buf,
TEMP_BUF_SIZE + BUFFER_LEN,
(char *)out_comp_buf,
OUT_COMP_BUF_SIZE,
9);
if (comp_sz <= TEMP_BUF_SIZE + BUFFER_LEN){
print_array(input_buf, TEMP_BUF_SIZE + BUFFER_LEN);
}
TEST_ASSERT_MESSAGE(comp_sz > TEMP_BUF_SIZE + BUFFER_LEN,
"TRNG_TEST_STEP3: concatenated buffer after reset was able to compress thus not random");
greentea_send_kv(MSG_TRNG_TEST_SUITE_ENDED, MSG_VALUE_DUMMY);
}
/*At the end of step 1 store trng buffer and reset the device*/
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0) {
int result = 0;
/*Using base64 to encode data sending from host*/
result = trng_EncodeBase64(buffer,
BUFFER_LEN,
(char *)out_comp_buf,
OUT_COMP_BUF_SIZE);
TEST_ASSERT_EQUAL(RESULT_SUCCESS, result);
greentea_send_kv(MSG_TRNG_BUFFER, (const char *)out_comp_buf);
}
delete[] out_comp_buf;
delete[] buffer;
delete[] input_buf;
delete[] temp_buf;
}
/*This method call first and second steps, it directs by the key received from the host*/
void trng_test()
{
greentea_send_kv(MSG_TRNG_READY, MSG_VALUE_DUMMY);
char key[MSG_KEY_LEN + 1] = { };
char *value = new char[MSG_VALUE_LEN + 1];
do {
memset(key, 0, MSG_KEY_LEN + 1);
memset(value, 0, MSG_VALUE_LEN + 1);
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
if (strcmp(key, MSG_TRNG_TEST_STEP1) == 0) {
/*create trng data buffer and try to compress it, store it for later checks*/
compress_and_compare(key, value);
}
if (strcmp(key, MSG_TRNG_TEST_STEP2) == 0) {
/*create another trng data buffer and concatenate it to the stored trng data buffer
try to compress them both*/
compress_and_compare(key, value);
}
} while (strcmp(key, MSG_TRNG_EXIT) != 0);
delete[] value;
}
Case cases[] = {
Case("TRNG: trng_test", trng_test),
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(100, "trng_reset");
return greentea_test_setup_handler(number_of_cases);
}
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
int main()
{
bool ret = !Harness::run(specification);
return ret;
}

View File

@ -0,0 +1,795 @@
//
// pithy.c
// http://github.com/johnezang/pithy
// Licensed under the terms of the BSD License, as specified below.
//
/*
Copyright (c) 2011, John Engelhart
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Zang Industries nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#if defined(__arm__) && defined(__ARM_NEON__)
#include <arm_neon.h>
#endif
#include "pithy.h"
#define kBlockLog 20ul
#define kBlockSize ((size_t)(1 << kBlockLog))
// The maximum size that can be compressed while still allowing for the worst case compression expansion.
#define PITHY_UNCOMPRESSED_MAX_LENGTH 0xdb6db6bfu // 0xdb6db6bf == 3681400511, or 3510.857 Mbytes.
typedef const char *pithy_hashOffset_t;
enum {
PITHY_LITERAL = 0,
PITHY_COPY_1_BYTE_OFFSET = 1,
PITHY_COPY_2_BYTE_OFFSET = 2,
PITHY_COPY_3_BYTE_OFFSET = 3
};
#if defined (__GNUC__) && (__GNUC__ >= 3)
#define PITHY_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__))
#define PITHY_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect))
#define PITHY_EXPECT_T(cond) PITHY_EXPECTED(cond, 1u)
#define PITHY_EXPECT_F(cond) PITHY_EXPECTED(cond, 0u)
#define PITHY_PREFETCH(ptr) __builtin_prefetch(ptr)
#else // defined (__GNUC__) && (__GNUC__ >= 3)
#define PITHY_ATTRIBUTES(attr, ...)
#define PITHY_EXPECTED(cond, expect) (cond)
#define PITHY_EXPECT_T(cond) (cond)
#define PITHY_EXPECT_F(cond) (cond)
#define PITHY_PREFETCH(ptr)
#endif // defined (__GNUC__) && (__GNUC__ >= 3)
#define PITHY_STATIC_INLINE static inline PITHY_ATTRIBUTES(always_inline)
#define PITHY_ALIGNED(x) PITHY_ATTRIBUTES(aligned(x))
#if defined(NS_BLOCK_ASSERTIONS) && !defined(NDEBUG)
#define NDEBUG
#endif
#ifdef NDEBUG
#define DCHECK(condition)
#else
#define DCHECK(condition) do { \
if(PITHY_EXPECT_F(!(condition))) { \
fprintf(stderr, "%s / %s @ %ld: Invalid parameter not satisfying: %s", \
__FILE__, __PRETTY_FUNCTION__, (long)__LINE__, #condition); fflush(stderr); \
abort(); \
} \
} while(0)
#endif
PITHY_STATIC_INLINE const char *pithy_Parse32WithLimit(const char *p, const char *l, size_t *OUTPUT);
PITHY_STATIC_INLINE char *pithy_Encode32(char *ptr, uint32_t v);
PITHY_STATIC_INLINE uint32_t pithy_GetUint32AtOffset(uint64_t v, uint32_t offset);
PITHY_STATIC_INLINE uint32_t pithy_HashBytes(uint32_t bytes, uint32_t shift);
PITHY_STATIC_INLINE size_t pithy_FindMatchLength(const char *s1, const char *s2, const char *s2_limit);
PITHY_STATIC_INLINE char *pithy_EmitLiteral(char *op, const char *literal, size_t len, int allow_fast_path);
PITHY_STATIC_INLINE char *pithy_EmitCopyGreaterThan63(char *op, size_t offset, size_t len);
PITHY_STATIC_INLINE char *pithy_EmitCopyLessThan63(char *op, size_t offset, size_t len);
PITHY_STATIC_INLINE char *pithy_EmitCopy(char *op, size_t offset, size_t len);
PITHY_STATIC_INLINE uint16_t pithy_Load16(const void *p) { uint16_t t; memcpy(&t, p, sizeof(t)); return (t); }
PITHY_STATIC_INLINE uint32_t pithy_Load32(const void *p) { uint32_t t; memcpy(&t, p, sizeof(t)); return (t); }
PITHY_STATIC_INLINE uint64_t pithy_Load64(const void *p) { uint64_t t; memcpy(&t, p, sizeof(t)); return (t); }
PITHY_STATIC_INLINE void pithy_Store16(void *p, uint16_t v) { memcpy(p, &v, sizeof(v)); }
PITHY_STATIC_INLINE void pithy_Store32(void *p, uint32_t v) { memcpy(p, &v, sizeof(v)); }
PITHY_STATIC_INLINE void pithy_Store64(void *p, uint64_t v) { memcpy(p, &v, sizeof(v)); }
#define pithy_Move64(dst,src) pithy_Store64(dst, pithy_Load64(src));
#define pithy_Move128(dst,src) pithy_Move64(dst, src); pithy_Move64(dst + 8ul, src + 8ul);
#ifdef __BIG_ENDIAN__
#ifdef _MSC_VER
#include <stdlib.h>
#define pithy_bswap_16(x) _byteswap_ushort(x)
#define pithy_bswap_32(x) _byteswap_ulong(x)
#define pithy_bswap_64(x) _byteswap_uint64(x)
#elif defined(__APPLE__)
// Mac OS X / Darwin features
#include <libkern/OSByteOrder.h>
#define pithy_bswap_16(x) OSSwapInt16(x)
#define pithy_bswap_32(x) OSSwapInt32(x)
#define pithy_bswap_64(x) OSSwapInt64(x)
#else
#include <byteswap.h>
#endif
#endif // __BIG_ENDIAN__
// Conversion functions.
#ifdef __BIG_ENDIAN__
#define pithy_FromHost16(x) ((uint16_t)pithy_bswap_16(x))
#define pithy_ToHost16(x) ((uint16_t)pithy_bswap_16(x))
#define pithy_FromHost32(x) ((uint32_t)pithy_bswap_32(x))
#define pithy_ToHost32(x) ((uint32_t)pithy_bswap_32(x))
#define pithy_IsLittleEndian() (0)
#else // !defined(__BIG_ENDIAN__)
#define pithy_FromHost16(x) ((uint16_t)(x))
#define pithy_ToHost16(x) ((uint16_t)(x))
#define pithy_FromHost32(x) ((uint32_t)(x))
#define pithy_ToHost32(x) ((uint32_t)(x))
#define pithy_IsLittleEndian() (1)
#endif // !defined(__BIG_ENDIAN__)
#define pithy_LoadHost16(p) pithy_ToHost16(pithy_Load16((const void *)(p)))
#define pithy_StoreHost16(p, v) pithy_Store16((void *)(p), pithy_FromHost16(v))
#define pithy_LoadHost32(p) pithy_ToHost32(pithy_Load32((p)))
#define pithy_StoreHost32(p, v) pithy_Store32((p), pithy_FromHost32(v))
PITHY_STATIC_INLINE void pithy_StoreHost24(char *p, uint32_t v)
{
*p++ = (v & 0xffu);
*p++ = ((v >> 8) & 0xffu);
*p++ = ((v >> 16) & 0xffu);
}
#if defined (__GNUC__) && (__GNUC__ >= 3)
#define pithy_Log2Floor(n) ({typeof(n) _n = (n); _n == 0 ? (int)-1 : (int)(31 ^ __builtin_clz(_n));})
#define pithy_FindLSBSetNonZero32(n) ((int)__builtin_ctz((uint32_t)(n)))
#define pithy_FindLSBSetNonZero64(n) ((int)__builtin_ctzll((uint64_t)(n)))
#define pithy_FindMSBSetNonZero32(n) ((int)__builtin_clz((uint32_t)(n)))
#define pithy_FindMSBSetNonZero64(n) ((int)__builtin_clzll((uint64_t)(n)))
#else // Portable versions, !GNUC || GNUC < 3
PITHY_STATIC_INLINE int pithy_Log2Floor(uint32_t n)
{
if (n == 0u) {
return (-1);
}
int i = 0, log = 0;
uint32_t value = n;
for (i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32_t x = value >> shift;
if (x != 0u) {
value = x;
log += shift;
}
}
DCHECK(value == 1);
return (log);
}
PITHY_STATIC_INLINE int pithy_FindLSBSetNonZero32(uint32_t n)
{
int i = 0, rc = 31, shift = 0;
for (i = 4, shift = 1 << 4; i >= 0; --i) {
const uint32_t x = n << shift;
if (x != 0u) {
n = x;
rc -= shift;
}
shift >>= 1;
}
return (rc);
}
PITHY_STATIC_INLINE int pithy_FindLSBSetNonZero64(uint64_t n)
{
const uint32_t bottomBits = n;
if (bottomBits == 0u) {
return (32 + pithy_FindLSBSetNonZero32((n >> 32)));
} else {
return (pithy_FindLSBSetNonZero32(bottomBits));
}
}
PITHY_STATIC_INLINE int pithy_FindMSBSetNonZero32(uint32_t n)
{
int i;
uint32_t x, rc = 32, shift = 1 << 4;
for (i = 3; i >= 0; --i) {
x = n >> shift;
if (x != 0) {
rc -= shift;
n = x;
}
shift >>= 1;
}
x = n >> shift;
return (rc - ((x != 0) ? 2 : n));
}
PITHY_STATIC_INLINE int pithy_FindMSBSetNonZero64(uint64_t n)
{
const uint32_t upperBits = n >> 32;
if (upperBits == 0u) {
return (32 + pithy_FindMSBSetNonZero32((n)));
} else {
return (pithy_FindMSBSetNonZero32(upperBits));
}
}
#endif // End portable versions.
PITHY_STATIC_INLINE const char *pithy_Parse32WithLimit(const char *p, const char *l, size_t *resultOut)
{
const unsigned char *ptr = (const unsigned char *)p, *limit = (const unsigned char *)l;
size_t resultShift = 0ul, result = 0ul;
uint32_t encodedByte = 0u;
for (resultShift = 0ul; resultShift <= 28ul; resultShift += 7ul) {
if (ptr >= limit) {
return (NULL);
}
encodedByte = *(ptr++);
result |= (encodedByte & 127u) << resultShift;
if (encodedByte < ((resultShift == 28ul) ? 16u : 128u)) {
goto done;
}
}
return (NULL);
done:
*resultOut = result;
return ((const char *)ptr);
}
PITHY_STATIC_INLINE char *pithy_Encode32(char *sptr, uint32_t v)
{
unsigned char *ptr = (unsigned char *)sptr;
if (v < (1u << 7)) {
*(ptr++) = v;
} else if (v < (1u << 14)) {
*(ptr++) = v | 0x80u;
*(ptr++) = (v >> 7);
} else if (v < (1u << 21)) {
*(ptr++) = v | 0x80u;
*(ptr++) = (v >> 7) | 0x80u;
*(ptr++) = (v >> 14);
} else if (v < (1u << 28)) {
*(ptr++) = v | 0x80u;
*(ptr++) = (v >> 7) | 0x80u;
*(ptr++) = (v >> 14) | 0x80u;
*(ptr++) = (v >> 21);
} else {
*(ptr++) = v | 0x80u;
*(ptr++) = (v >> 7) | 0x80u;
*(ptr++) = (v >> 14) | 0x80u;
*(ptr++) = (v >> 21) | 0x80u;
*(ptr++) = (v >> 28);
}
return ((char *)ptr);
}
PITHY_STATIC_INLINE uint32_t pithy_GetUint32AtOffset(uint64_t v, uint32_t offset)
{
DCHECK(offset <= 4);
return (v >> (pithy_IsLittleEndian() ? (8u * offset) : (32u - (8u * offset))));
}
PITHY_STATIC_INLINE uint32_t pithy_HashBytes(uint32_t bytes, uint32_t shift)
{
uint32_t kMul = 0x1e35a7bdU;
return ((bytes * kMul) >> shift);
}
PITHY_STATIC_INLINE size_t pithy_FindMatchLength(const char *s1, const char *s2, const char *s2_limit)
{
DCHECK(s2_limit >= s2);
const char *ms1 = s1, *ms2 = s2;
#if defined(__LP64__)
while (PITHY_EXPECT_T(ms2 < (s2_limit - 8ul))) {
uint64_t x = pithy_Load64(ms1) ^ pithy_Load64(ms2);
if (PITHY_EXPECT_F(x == 0ul)) {
ms1 += 8ul;
ms2 += 8ul;
} else {
return ((ms1 - s1) + ((unsigned int)(pithy_IsLittleEndian() ? (pithy_FindLSBSetNonZero64(x) >> 3) :
(pithy_FindMSBSetNonZero64(x) >> 3))));
}
}
#else
while (PITHY_EXPECT_T(ms2 < (s2_limit - 4u ))) {
uint32_t x = pithy_Load32(ms1) ^ pithy_Load32(ms2);
if (PITHY_EXPECT_F(x == 0u)) {
ms1 += 4u;
ms2 += 4u;
} else {
return ((ms1 - s1) + ((unsigned int)(pithy_IsLittleEndian() ?
(pithy_FindLSBSetNonZero32(x) >> 3) : (pithy_FindMSBSetNonZero32(x) >> 3))));
}
}
#endif
while (PITHY_EXPECT_T(ms2 < s2_limit)) {
if (PITHY_EXPECT_T(*ms1 == *ms2)) {
ms1++;
ms2++;
} else {
return (ms1 - s1);
}
}
return (ms1 - s1);
}
PITHY_STATIC_INLINE char *pithy_EmitLiteral(char *op, const char *literal, size_t len, int allow_fast_path)
{
int n = len - 1l;
if (PITHY_EXPECT_T(n < 60l)) {
*op++ = PITHY_LITERAL | (n << 2);
if (PITHY_EXPECT_T(allow_fast_path) && PITHY_EXPECT_T(len <= 16ul)) {
pithy_Move128(op, literal);
return (op + len);
}
} else {
char *base = op;
int count = 0;
op++;
while (n > 0l) {
*op++ = n & 0xff;
n >>= 8;
count++;
}
DCHECK((count >= 1) && (count <= 4));
*base = PITHY_LITERAL | ((59 + count) << 2);
}
memcpy(op, literal, len);
return (op + len);
}
PITHY_STATIC_INLINE char *pithy_EmitCopyGreaterThan63(char *op, size_t offset, size_t len)
{
DCHECK((len < 65536ul) && (len >= 63ul) && (offset < kBlockSize));
if (PITHY_EXPECT_T(offset < 65536ul)) {
if (PITHY_EXPECT_T(len < (256ul + 63ul))) {
*op++ = PITHY_COPY_2_BYTE_OFFSET | (62 << 2);
pithy_StoreHost16(op, offset);
op += 2ul;
*op++ = (len - 63ul);
} else {
*op++ = PITHY_COPY_2_BYTE_OFFSET | (63 << 2);
pithy_StoreHost16(op, offset);
op += 2ul;
pithy_StoreHost16(op, len);
op += 2ul;
}
} else {
if (PITHY_EXPECT_T(len < (256ul + 63ul))) {
*op++ = PITHY_COPY_3_BYTE_OFFSET | (62 << 2);
pithy_StoreHost24(op, offset);
op += 3ul;
*op++ = (len - 63ul);
} else {
*op++ = PITHY_COPY_3_BYTE_OFFSET | (63 << 2);
pithy_StoreHost24(op, offset);
op += 3ul;
pithy_StoreHost16(op, len);
op += 2ul;
}
}
return (op);
}
PITHY_STATIC_INLINE char *pithy_EmitCopyLessThan63(char *op, size_t offset, size_t len)
{
DCHECK((len < 63ul) && (len >= 4ul) && (offset < kBlockSize));
if (PITHY_EXPECT_T(len < 12ul) && PITHY_EXPECT_T(offset < 2048ul)) {
int lenMinus4 = len - 4l;
DCHECK(lenMinus4 < 8l);
*op++ = PITHY_COPY_1_BYTE_OFFSET | (lenMinus4 << 2) | ((offset >> 8) << 5);
*op++ = offset & 0xff;
} else {
if (PITHY_EXPECT_T(offset < 65536ul)) {
*op++ = PITHY_COPY_2_BYTE_OFFSET | ((len - 1ul) << 2);
pithy_StoreHost16(op, offset);
op += 2ul;
} else {
*op++ = PITHY_COPY_3_BYTE_OFFSET | ((len - 1ul) << 2);
pithy_StoreHost24(op, offset);
op += 3ul;
}
}
return (op);
}
PITHY_STATIC_INLINE char *pithy_EmitCopy(char *op, size_t offset, size_t len)
{
while (PITHY_EXPECT_F(len >= 63ul)) {
op = pithy_EmitCopyGreaterThan63(op, offset, (len >= 65539ul) ? 65535ul : len);
len -= (len >= 65539ul) ? 65535ul : len;
}
DCHECK((len > 0ul) ? ((len >= 4ul) && (len < 63ul)) : 1);
if ( PITHY_EXPECT_T(len > 0ul)) {
op = pithy_EmitCopyLessThan63(op, offset, len);
len -= len;
}
return (op);
}
size_t pithy_MaxCompressedLength(size_t inputLength)
{
return ((inputLength >= PITHY_UNCOMPRESSED_MAX_LENGTH) ? 0ul : 32ul + inputLength + (inputLength / 6ul));
}
size_t pithy_Compress(const char *uncompressed,
size_t uncompressedLength,
char *compressedOut,
size_t compressedOutLength,
int compressionLevel)
{
if ((pithy_MaxCompressedLength(uncompressedLength) > compressedOutLength) ||
(uncompressedLength >= PITHY_UNCOMPRESSED_MAX_LENGTH) ||
(uncompressedLength == 0ul)) {
return (0ul);
}
char *compressedPtr = compressedOut;
size_t hashTableSize = 0x100ul, maxHashTableSize = 1 << (12ul + (((compressionLevel < 0) ? 0 :
(compressionLevel > 9) ? 9 : compressionLevel) / 2ul));
while ((hashTableSize < maxHashTableSize) && (hashTableSize < uncompressedLength)) {
hashTableSize <<= 1;
}
pithy_hashOffset_t stackHashTable[hashTableSize], *heapHashTable = NULL, *hashTable = stackHashTable;
if ((sizeof(pithy_hashOffset_t) * hashTableSize) >= (1024ul * 128ul)) {
if ((heapHashTable = malloc(sizeof(pithy_hashOffset_t) * hashTableSize)) == NULL) {
return (0ul);
}
hashTable = heapHashTable;
}
size_t x = 0ul;
for (x = 0ul; x < hashTableSize; x++) {
hashTable[x] = uncompressed;
}
#ifndef NDEBUG
char *const compressedOutEnd = compressedOut + compressedOutLength;
#endif
compressedPtr = pithy_Encode32(compressedPtr, uncompressedLength);
DCHECK(compressedPtr <= compressedOutEnd);
{
const char *uncompressedPtr = uncompressed;
const char *uncompressedEnd = uncompressed + uncompressedLength;
const char *nextEmitUncompressedPtr = uncompressedPtr;
DCHECK((hashTableSize & (hashTableSize - 1l)) == 0);
const int shift = 32 - pithy_Log2Floor(hashTableSize);
DCHECK((UINT32_MAX >> shift) == (hashTableSize - 1l));
size_t skip = 32ul;
if (PITHY_EXPECT_T(uncompressedLength >= 15ul)) {
const char *uncompressedEndLimit = uncompressed + uncompressedLength - 15ul;
uint32_t uncompressedBytes;
uint32_t nextUncompressedBytes = pithy_Load32(++uncompressedPtr);
uint32_t nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift);
while (1) {
DCHECK(nextEmitUncompressedPtr < uncompressedPtr);
const char *nextUncompressedPtr = uncompressedPtr, *matchCandidatePtr = NULL;
skip = (((skip - 32ul) * 184ul) >> 8) + 32ul;
do {
uncompressedPtr = nextUncompressedPtr;
uncompressedBytes = nextUncompressedBytes;
uint32_t uncompressedBytesHash = nextUncompressedBytesHash;
DCHECK(uncompressedBytesHash == pithy_HashBytes(uncompressedBytes, shift));
size_t skipBytesBetweenHashLookups = skip >> 5;
skip += ((skip * 7ul) >> 11) + 1ul;
nextUncompressedPtr = uncompressedPtr + skipBytesBetweenHashLookups;
if (PITHY_EXPECT_F(nextUncompressedPtr > uncompressedEndLimit)) {
goto emit_remainder;
}
nextUncompressedBytes = pithy_Load32(nextUncompressedPtr);
nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift);
matchCandidatePtr = hashTable[uncompressedBytesHash];
DCHECK((matchCandidatePtr >= uncompressed) && (matchCandidatePtr < uncompressedPtr));
hashTable[uncompressedBytesHash] = uncompressedPtr;
} while ((PITHY_EXPECT_T(uncompressedBytes != pithy_Load32(matchCandidatePtr))) ||
PITHY_EXPECT_F((uncompressedPtr - matchCandidatePtr) >= ((int)(kBlockSize - 2ul))));
DCHECK((nextEmitUncompressedPtr + 16ul) <= uncompressedEnd);
compressedPtr = pithy_EmitLiteral(compressedPtr,
nextEmitUncompressedPtr,
uncompressedPtr - nextEmitUncompressedPtr,
1);
DCHECK(compressedPtr <= compressedOutEnd);
uint64_t uncompressedBytes64 = 0ul;
do {
if (compressionLevel > 2) {
DCHECK((uncompressedPtr + 5ul) <= uncompressedEnd);
uncompressedBytes64 = pithy_Load64((uint64_t*)uncompressedPtr + 1ul);
hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 0u), shift)] =
uncompressedPtr + 1ul;
if (compressionLevel > 4) {
hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 1u), shift)] =
uncompressedPtr + 2ul;
}
}
DCHECK((matchCandidatePtr >= uncompressed) &&
(matchCandidatePtr <= uncompressedPtr) &&
((matchCandidatePtr + 4ul) <= uncompressedEnd) &&
((uncompressedPtr + 4ul) <= uncompressedEnd));
size_t matchCandidateLength = 4ul + pithy_FindMatchLength(matchCandidatePtr + 4ul,
uncompressedPtr + 4ul,
uncompressedEnd);
DCHECK(((matchCandidatePtr + matchCandidateLength) >= uncompressed) &&
((matchCandidatePtr + matchCandidateLength) <= uncompressedEnd));
DCHECK(0 == memcmp(uncompressedPtr, matchCandidatePtr, matchCandidateLength));
compressedPtr = pithy_EmitCopy(compressedPtr,
uncompressedPtr - matchCandidatePtr,
matchCandidateLength);
DCHECK(compressedPtr <= compressedOutEnd);
uncompressedPtr += matchCandidateLength;
DCHECK(uncompressedPtr <= uncompressedEnd);
nextEmitUncompressedPtr = uncompressedPtr;
if (PITHY_EXPECT_F(uncompressedPtr >= uncompressedEndLimit)) {
goto emit_remainder;
}
DCHECK(((uncompressedPtr - 3ul) >= uncompressed) && (uncompressedPtr <= uncompressedEnd));
uncompressedBytes64 = pithy_Load64((uint64_t*)uncompressedPtr - 3ul);
if (compressionLevel > 0) {
if (compressionLevel > 8) {
hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 0u), shift)] =
uncompressedPtr - 3ul;
}
if (compressionLevel > 6) {
hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 1u), shift)] =
uncompressedPtr - 2ul;
}
hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 2u), shift)] =
uncompressedPtr - 1ul;
}
uncompressedBytes = pithy_GetUint32AtOffset(uncompressedBytes64, 3u);
uint32_t uncompressedBytesHash = pithy_HashBytes(uncompressedBytes, shift);
matchCandidatePtr = hashTable[uncompressedBytesHash];
DCHECK((matchCandidatePtr >= uncompressed) && (matchCandidatePtr < uncompressedPtr));
hashTable[uncompressedBytesHash] = uncompressedPtr;
} while (PITHY_EXPECT_F(uncompressedBytes == pithy_Load32(matchCandidatePtr)) &&
PITHY_EXPECT_T((uncompressedPtr - matchCandidatePtr) < ((int)(kBlockSize - 2ul))));
nextUncompressedBytes = pithy_GetUint32AtOffset(uncompressedBytes64, 4u);
nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift);
uncompressedPtr++;
}
}
emit_remainder:
if (nextEmitUncompressedPtr < uncompressedEnd) {
compressedPtr = pithy_EmitLiteral(compressedPtr,
nextEmitUncompressedPtr,
uncompressedEnd - nextEmitUncompressedPtr,
0);
}
}
pithy_Store32(compressedPtr, 0);
compressedPtr += 4;
DCHECK((size_t)(compressedPtr - compressedOut) <= compressedOutLength);
if (heapHashTable != NULL) {
free(heapHashTable);
heapHashTable = NULL;
hashTable = NULL;
}
return (compressedPtr - compressedOut);
}
static const uint32_t pithy_wordmask[] = {
0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu
};
static const uint16_t pithy_charTable[256] = {
0x0001, 0x0804, 0x1001, 0x1801, 0x0002, 0x0805, 0x1002, 0x1802,
0x0003, 0x0806, 0x1003, 0x1803, 0x0004, 0x0807, 0x1004, 0x1804,
0x0005, 0x0808, 0x1005, 0x1805, 0x0006, 0x0809, 0x1006, 0x1806,
0x0007, 0x080a, 0x1007, 0x1807, 0x0008, 0x080b, 0x1008, 0x1808,
0x0009, 0x0904, 0x1009, 0x1809, 0x000a, 0x0905, 0x100a, 0x180a,
0x000b, 0x0906, 0x100b, 0x180b, 0x000c, 0x0907, 0x100c, 0x180c,
0x000d, 0x0908, 0x100d, 0x180d, 0x000e, 0x0909, 0x100e, 0x180e,
0x000f, 0x090a, 0x100f, 0x180f, 0x0010, 0x090b, 0x1010, 0x1810,
0x0011, 0x0a04, 0x1011, 0x1811, 0x0012, 0x0a05, 0x1012, 0x1812,
0x0013, 0x0a06, 0x1013, 0x1813, 0x0014, 0x0a07, 0x1014, 0x1814,
0x0015, 0x0a08, 0x1015, 0x1815, 0x0016, 0x0a09, 0x1016, 0x1816,
0x0017, 0x0a0a, 0x1017, 0x1817, 0x0018, 0x0a0b, 0x1018, 0x1818,
0x0019, 0x0b04, 0x1019, 0x1819, 0x001a, 0x0b05, 0x101a, 0x181a,
0x001b, 0x0b06, 0x101b, 0x181b, 0x001c, 0x0b07, 0x101c, 0x181c,
0x001d, 0x0b08, 0x101d, 0x181d, 0x001e, 0x0b09, 0x101e, 0x181e,
0x001f, 0x0b0a, 0x101f, 0x181f, 0x0020, 0x0b0b, 0x1020, 0x1820,
0x0021, 0x0c04, 0x1021, 0x1821, 0x0022, 0x0c05, 0x1022, 0x1822,
0x0023, 0x0c06, 0x1023, 0x1823, 0x0024, 0x0c07, 0x1024, 0x1824,
0x0025, 0x0c08, 0x1025, 0x1825, 0x0026, 0x0c09, 0x1026, 0x1826,
0x0027, 0x0c0a, 0x1027, 0x1827, 0x0028, 0x0c0b, 0x1028, 0x1828,
0x0029, 0x0d04, 0x1029, 0x1829, 0x002a, 0x0d05, 0x102a, 0x182a,
0x002b, 0x0d06, 0x102b, 0x182b, 0x002c, 0x0d07, 0x102c, 0x182c,
0x002d, 0x0d08, 0x102d, 0x182d, 0x002e, 0x0d09, 0x102e, 0x182e,
0x002f, 0x0d0a, 0x102f, 0x182f, 0x0030, 0x0d0b, 0x1030, 0x1830,
0x0031, 0x0e04, 0x1031, 0x1831, 0x0032, 0x0e05, 0x1032, 0x1832,
0x0033, 0x0e06, 0x1033, 0x1833, 0x0034, 0x0e07, 0x1034, 0x1834,
0x0035, 0x0e08, 0x1035, 0x1835, 0x0036, 0x0e09, 0x1036, 0x1836,
0x0037, 0x0e0a, 0x1037, 0x1837, 0x0038, 0x0e0b, 0x1038, 0x1838,
0x0039, 0x0f04, 0x1039, 0x1839, 0x003a, 0x0f05, 0x103a, 0x183a,
0x003b, 0x0f06, 0x103b, 0x183b, 0x003c, 0x0f07, 0x103c, 0x183c,
0x0801, 0x0f08, 0x103d, 0x183d, 0x1001, 0x0f09, 0x103e, 0x183e,
0x1801, 0x0f0a, 0x103f, 0x183f, 0x2001, 0x0f0b, 0x1040, 0x1840
};
int pithy_GetDecompressedLength(const char *compressed, size_t compressedLength, size_t *decompressedOutLengthResult)
{
const char *compressedEnd = compressed + compressedLength;
size_t decompressedLength = 0ul;
if (pithy_Parse32WithLimit(compressed, compressedEnd, &decompressedLength) != NULL) {
*decompressedOutLengthResult = decompressedLength;
return (1);
} else {
return (0);
}
}
int pithy_Decompress(const char *compressed, size_t compressedLength, char *decompressedOut,
size_t decompressedOutLength)
{
const char *nextCompressedPtr = NULL, *compressedEnd = (compressed + compressedLength);
size_t parsedDecompressedLength = 0ul;
if (((nextCompressedPtr = pithy_Parse32WithLimit(compressed, compressedEnd, &parsedDecompressedLength)) == NULL) ||
(parsedDecompressedLength > decompressedOutLength)) {
return (0);
}
char *decompressedPtr = decompressedOut, *decompressedEnd = decompressedOut + parsedDecompressedLength;
while (1) {
const char *compressedPtr = nextCompressedPtr;
DCHECK(compressedPtr <= compressedEnd);
if (PITHY_EXPECT_F(compressedPtr >= compressedEnd)) {
break;
}
const unsigned char c = *((const unsigned char *)(compressedPtr++));
const unsigned char cLowerBits = (c & 0x3u);
const int spaceLeft = (decompressedEnd - decompressedPtr);
if ((cLowerBits == PITHY_LITERAL)) {
size_t literalLength = (c >> 2) + 1;
if (PITHY_EXPECT_T(literalLength <= 16ul) && PITHY_EXPECT_T((compressedEnd - compressedPtr) >= 16l) &&
PITHY_EXPECT_T(spaceLeft >= 16l)) {
pithy_Move128(decompressedPtr, compressedPtr);
} else {
if (PITHY_EXPECT_F(literalLength > 60)) {
if (PITHY_EXPECT_F((compressedPtr + 4) > compressedEnd)) {
break;
}
size_t literalLengthBytes = literalLength - 60;
literalLength = (pithy_LoadHost32(compressedPtr) & pithy_wordmask[literalLengthBytes]) + 1;
compressedPtr += literalLengthBytes;
}
if (PITHY_EXPECT_F(spaceLeft < (int)literalLength) ||
PITHY_EXPECT_F((compressedPtr + literalLength) > compressedEnd)) {
break;
}
memcpy(decompressedPtr, compressedPtr, literalLength);
}
nextCompressedPtr = compressedPtr + literalLength;
decompressedPtr += literalLength;
} else {
const uint32_t entry = pithy_charTable[c];
const size_t trailer = pithy_LoadHost32(compressedPtr) & pithy_wordmask[cLowerBits];
size_t length = entry & 0xffu;
const size_t copyOffset = ((entry & 0x700u) + trailer);
compressedPtr += cLowerBits;
DCHECK((compressedPtr <= compressedEnd) && (copyOffset > 0ul) && (spaceLeft > 0l) && (length > 0ul));
if (PITHY_EXPECT_F((decompressedPtr - decompressedOut) <= ((int)copyOffset - 1l))) {
break;
}
if (PITHY_EXPECT_T(length <= 16ul) &&
PITHY_EXPECT_T(copyOffset >= 16ul) &&
PITHY_EXPECT_T(spaceLeft >= 16l)) {
pithy_Move128(decompressedPtr, decompressedPtr - copyOffset);
} else {
if (PITHY_EXPECT_F(length >= 63ul)) {
if (PITHY_EXPECT_T(length == 63ul)) {
if (PITHY_EXPECT_F((compressedPtr + 1) > compressedEnd)) {
break;
}
length = (*((unsigned char *)compressedPtr++)) + 63ul;
} else {
if (PITHY_EXPECT_F((compressedPtr + 2) > compressedEnd)) {
break;
}
length = pithy_LoadHost16(compressedPtr);
compressedPtr += 2ul;
}
}
char *copyFrom = decompressedPtr - copyOffset, *copyTo = decompressedPtr;
int copyLength = (int)length;
if (PITHY_EXPECT_F(copyLength > 256l) && PITHY_EXPECT_T(copyOffset > (size_t)copyLength)) {
if (PITHY_EXPECT_F(spaceLeft < copyLength)) {
break;
}
memcpy(copyTo, copyFrom, copyLength);
} else {
if (PITHY_EXPECT_T(spaceLeft >= (copyLength + 24)) && PITHY_EXPECT_T(copyLength > 0l)) {
while ((copyTo - copyFrom) < 16l) {
pithy_Move128(copyTo, copyFrom);
copyLength -= copyTo - copyFrom;
copyTo += copyTo - copyFrom;
}
while (copyLength > 0l) {
pithy_Move128(copyTo, copyFrom);
copyFrom += 16l;
copyTo += 16l;
copyLength -= 16l;
}
} else {
if (PITHY_EXPECT_F(spaceLeft < copyLength) || PITHY_EXPECT_F(copyLength <= 0l)) {
break;
}
do {
*copyTo++ = *copyFrom++;
} while (--copyLength > 0l);
}
}
}
nextCompressedPtr = compressedPtr;
decompressedPtr += length;
}
}
return (decompressedPtr == decompressedEnd);
}

View File

@ -0,0 +1,63 @@
//
// pithy.h
// http://github.com/johnezang/pithy
// Licensed under the terms of the BSD License, as specified below.
//
/*
Copyright (c) 2011, John Engelhart
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Zang Industries nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _PITHY_H_
#define _PITHY_H_
// compressionLevel >= 0 && compressionLevel <= 9. Values out side this range will be clamped to this range.
size_t pithy_Compress (const char *uncompressed, size_t uncompressedLength, char *compressedOut,
size_t compressedOutLength, int compressionLevel);
int pithy_Decompress(const char *compressed, size_t compressedLength, char *decompressedOut,
size_t decompressedOutLength);
size_t pithy_MaxCompressedLength(size_t inputLength);
int pithy_GetDecompressedLength(const char *compressed, size_t compressedLength,
size_t *decompressedOutLengthResult);
#endif // _PITHY_H_
#ifdef __cplusplus
} // extern "C"
#endif