mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			
						commit
						a0a9b54e97
					
				| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
		Loading…
	
		Reference in New Issue