Some smaller changes and code cleanups

This commit is contained in:
WolverinDEV 2021-02-05 17:20:38 +01:00
parent d213c0ade0
commit ac48d30696
22 changed files with 1221 additions and 3516 deletions

View File

@ -101,12 +101,7 @@ set(SOURCE_FILES
#Logger #Logger
src/log/LogUtils.cpp src/log/LogUtils.cpp
src/log/LogSinks.cpp src/log/LogSinks.cpp
src/qlz/QuickLZ.cpp src/qlz/QuickLZ.cpp
src/qlz/QuickLZ_L3.cpp
src/qlz/QuickLZ_L1.cpp
src/converters/converter.cpp src/converters/converter.cpp
src/query/command3.cpp src/query/command3.cpp
@ -155,7 +150,6 @@ set(HEADER_FILES
src/misc/memtracker.h src/misc/memtracker.h
src/misc/strobf.h src/misc/strobf.h
src/log/translation.h
src/log/LogUtils.h src/log/LogUtils.h
src/PermissionManager.h src/PermissionManager.h

View File

@ -12,7 +12,7 @@ using namespace ts;
#define define_error_description(type, description) \ #define define_error_description(type, description) \
{ error::type, str(type), description } { error::type, str(type), description }
const std::vector<ErrorType> ts::avariableErrors = { const std::vector<ErrorType> ts::availableErrors = {
{0x0000, "ok" , "ok" }, {0x0000, "ok" , "ok" },
{0x0001, "undefined" , "undefined error" }, {0x0001, "undefined" , "undefined error" },
{0x0002, "not_implemented" , "not implemented" }, {0x0002, "not_implemented" , "not implemented" },
@ -201,19 +201,19 @@ const std::vector<ErrorType> ts::avariableErrors = {
{0xFFFF, "custom_error" , "costume" }, {0xFFFF, "custom_error" , "costume" },
}; };
ErrorType ErrorType::Success = avariableErrors[0]; ErrorType ErrorType::Success = availableErrors[0];
ErrorType ErrorType::Costume = findError("custom_error"); ErrorType ErrorType::Costume = findError("custom_error");
ErrorType ErrorType::VSError = findError("vs_critical"); ErrorType ErrorType::VSError = findError("vs_critical");
ErrorType ErrorType::DBEmpty = findError("database_empty_result"); ErrorType ErrorType::DBEmpty = findError("database_empty_result");
ErrorType ts::findError(uint16_t errorId){ ErrorType ts::findError(uint16_t errorId){
for(auto elm : avariableErrors) for(auto elm : availableErrors)
if(elm.errorId == errorId) return elm; if(elm.errorId == errorId) return elm;
return ErrorType{errorId, "undefined", "undefined"}; return ErrorType{errorId, "undefined", "undefined"};
} }
ErrorType ts::findError(std::string key){ ErrorType ts::findError(std::string key){
for(auto elm : avariableErrors) for(auto elm : availableErrors)
if(elm.name == key) return elm; if(elm.name == key) return elm;
return ErrorType{1, key, "undefined"}; return ErrorType{1, key, "undefined"};
} }

View File

@ -10,10 +10,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#define _NDEBUG
namespace ts { namespace ts {
struct CommandResult;
namespace permission { namespace permission {
enum PermissionType : uint16_t; enum PermissionType : uint16_t;
} }
@ -365,8 +362,8 @@ namespace ts {
explicit command_result(command_result_bulk&&); explicit command_result(command_result_bulk&&);
#ifndef _NDEBUG #if !defined(_NDEBUG) && false
/* if we're not using any debug we dont have to use a deconstructor. A deconstructor prevent a direct uint64_t return as described above */ /* if we're not using any debug we dont have to use a destructor. A destructor prevent a direct uint64_t return as described above */
~command_result() { ~command_result() {
assert(this->data == 0); assert(this->data == 0);
} }
@ -457,15 +454,15 @@ namespace ts {
std::string message; std::string message;
bool operator==(const ErrorType& ref) const { bool operator==(const ErrorType& ref) const {
return errorId == ref.errorId; return this->errorId == ref.errorId;
} }
bool operator!=(const ErrorType& ref) const { return !operator==(ref); } bool operator!=(const ErrorType& ref) const { return !operator==(ref); }
ErrorType& operator=(const ErrorType& ref) { ErrorType& operator=(const ErrorType& ref) {
errorId = ref.errorId; this->errorId = ref.errorId;
name = ref.name; this->name = ref.name;
message = ref.message; this->message = ref.message;
return *this; return *this;
} }
@ -477,9 +474,7 @@ namespace ts {
} }
}; };
extern const std::vector<ErrorType> avariableErrors; extern const std::vector<ErrorType> availableErrors;
extern ErrorType findError(uint16_t errorId); extern ErrorType findError(uint16_t errorId);
extern ErrorType findError(std::string key); extern ErrorType findError(std::string key);
} }
#undef _NDEBUG

View File

@ -34,7 +34,7 @@ CONVERTER_PRIMITIVE_ST(float, std::stof(std::string{str}));
CONVERTER_PRIMITIVE_ST(double, std::stod(std::string{str})); CONVERTER_PRIMITIVE_ST(double, std::stod(std::string{str}));
CONVERTER_PRIMITIVE_ST(long_double, std::stold(std::string{str})); CONVERTER_PRIMITIVE_ST(long_double, std::stold(std::string{str}));
#if __x86_64__ #if UINTPTR_WIDTH >= 64
CONVERTER_PRIMITIVE_ST(long_long_unsigned_int_t, std::stoull(std::string{str})); CONVERTER_PRIMITIVE_ST(long_long_unsigned_int_t, std::stoull(std::string{str}));
#endif #endif

View File

@ -98,8 +98,9 @@ namespace logger {
const auto& mapping = level_mapping; const auto& mapping = level_mapping;
#endif #endif
size_t level = msg.level.value; size_t level = msg.level.value;
if(level >= mapping.size()) if(level >= mapping.size()) {
level = mapping.size() - 1; level = mapping.size() - 1;
}
append(mapping[level]); append(mapping[level]);
} }
@ -122,12 +123,13 @@ namespace logger {
index = found; index = found;
append(spdlog::details::os::default_eol); append(spdlog::details::os::default_eol);
if(++index) if(++index) {
dest.append(prefix_begin, prefix_end); dest.append(prefix_begin, prefix_end);
else } else {
break; break;
} }
} }
}
[[nodiscard]] std::unique_ptr<formatter> LogFormatter::clone() const { [[nodiscard]] std::unique_ptr<formatter> LogFormatter::clone() const {
return std::make_unique<LogFormatter>(this->_colored); return std::make_unique<LogFormatter>(this->_colored);

View File

@ -1,5 +0,0 @@
//
// Created by wolverindev on 12.06.18.
//
#include "translation.h"

View File

@ -1,13 +0,0 @@
#pragma once
namespace tr {
enum Messages {
kick_invalid_badges,
kick_invalid_packet,
kick_invalid_command,
crash_instance,
shutdown_instance,
shutdown_server,
};
}

View File

@ -29,11 +29,13 @@ namespace lookup {
bucket_t* bucket = &this->buckets[hash]; bucket_t* bucket = &this->buckets[hash];
std::lock_guard lock{this->bucket_locks[hash]}; std::lock_guard lock{this->bucket_locks[hash]};
while(bucket->entry_count == kBukkitSize && bucket->next) while(bucket->entry_count == kBukkitSize && bucket->next) {
bucket = bucket->next; bucket = bucket->next;
}
if(bucket->entry_count == kBukkitSize) if(bucket->entry_count == kBukkitSize) {
bucket = (bucket->next = new bucket_t{}); bucket = (bucket->next = new bucket_t{});
}
auto& entry = bucket->entries[bucket->entry_count++]; auto& entry = bucket->entries[bucket->entry_count++];
addr_converter{}(entry.address, address); addr_converter{}(entry.address, address);
@ -55,9 +57,11 @@ namespace lookup {
size_t entry_index; size_t entry_index;
do { do {
for(entry_index = 0; entry_index < bucket->entry_count; entry_index++) for(entry_index = 0; entry_index < bucket->entry_count; entry_index++) {
if(auto& entry{bucket->entries[entry_index]}; cmp(entry.address, addr)) if(auto& entry{bucket->entries[entry_index]}; cmp(entry.address, addr)) {
goto entry_found; goto entry_found;
}
}
} while((bucket = bucket->next)); } while((bucket = bucket->next));
/* entry hasn't been found */ /* entry hasn't been found */
@ -66,8 +70,9 @@ namespace lookup {
entry_found: entry_found:
next_bucket = bucket; next_bucket = bucket;
while(next_bucket->next && next_bucket->next->entry_count > 0) while(next_bucket->next && next_bucket->next->entry_count > 0) {
next_bucket = next_bucket->next; next_bucket = next_bucket->next;
}
/* swap the entry with the last entry and just remove the value */ /* swap the entry with the last entry and just remove the value */
next_bucket->entry_count--; next_bucket->entry_count--;
@ -76,9 +81,10 @@ namespace lookup {
} }
inline void cleanup() { inline void cleanup() {
for(size_t bucket_index{0}; bucket_index < kBukkitListSize; bucket_index++) for(size_t bucket_index{0}; bucket_index < kBukkitListSize; bucket_index++) {
cleanup_bucket(bucket_index); cleanup_bucket(bucket_index);
} }
}
inline void cleanup_bucket(size_t index) { inline void cleanup_bucket(size_t index) {
bucket_t* delete_head; bucket_t* delete_head;
@ -87,8 +93,9 @@ namespace lookup {
auto& bucket = this->buckets[index]; auto& bucket = this->buckets[index];
bucket_t* prev{nullptr}, curr{&bucket}; bucket_t* prev{nullptr}, curr{&bucket};
while(curr.entry_count > 0 && curr.next) while(curr.entry_count > 0 && curr.next) {
prev = std::exchange(curr, curr.next); prev = std::exchange(curr, curr.next);
}
if(curr.entry_count == 0) { if(curr.entry_count == 0) {
prev->next = nullptr; prev->next = nullptr;
@ -119,9 +126,11 @@ namespace lookup {
{ {
std::lock_guard lock{this->bucket_locks[hash]}; std::lock_guard lock{this->bucket_locks[hash]};
do { do {
for(size_t index{0}; index < bucket->entry_count; index++) for(size_t index{0}; index < bucket->entry_count; index++) {
if(auto& entry{bucket->entries[index]}; cmp(entry.address, addr)) if(auto& entry{bucket->entries[index]}; cmp(entry.address, addr)) {
return entry.entry; return entry.entry;
}
}
} while((bucket = bucket->next)); } while((bucket = bucket->next));
} }

View File

@ -3,42 +3,11 @@
#define QLZ_COMPRESSION_LEVEL 1 #define QLZ_COMPRESSION_LEVEL 1
#define QLZ_MEMORY_SAFE #define QLZ_MEMORY_SAFE
#include "qlz/QuickLZ.h" #include "qlz/QuickLZ.h"
#include "buffers.h"
using namespace ts; using namespace ts;
using namespace ts::connection;
using namespace std; using namespace std;
namespace ts::compression { namespace ts::compression {
class thread_buffer {
public:
void* get_buffer(size_t size) {
if(size > 1024 * 1024 *5) /* we don't want to keep such big buffers in memory */
return malloc(size);
if(this->buffer_length < size) {
free(this->buffer_ptr);
size = std::max(size, (size_t) 1024);
this->buffer_ptr = malloc(size);
this->buffer_length = size;
}
return buffer_ptr;
}
void free_buffer(void* ptr) {
if(ptr == this->buffer_ptr) return;
free(ptr);
}
~thread_buffer() {
free(this->buffer_ptr);
}
private:
void* buffer_ptr{nullptr};
size_t buffer_length{0};
};
class qlz_states { class qlz_states {
public: public:
qlz_states() noexcept { qlz_states() noexcept {
@ -53,25 +22,28 @@ namespace ts::compression {
qlz_state_compress* state_compress{nullptr}; qlz_state_compress* state_compress{nullptr};
qlz_state_decompress* state_decompress{nullptr}; qlz_state_decompress* state_decompress{nullptr};
private:
}; };
thread_local thread_buffer qlz_buffer{};
thread_local qlz_states qlz_states{}; thread_local qlz_states qlz_states{};
size_t qlz_decompressed_size(const void* payload, size_t payload_length) { size_t qlz_decompressed_size(const void* payload, size_t payload_length) {
if(payload_length < 9) return 0; /* payload too small */ if(payload_length < 9) {
return 0; /* payload too small */
}
return qlz_size_decompressed((char*) payload) + 400; return qlz_size_decompressed((char*) payload) + 400;
} }
bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size) { bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size) {
if(!qlz_states.state_decompress) return false; if(!qlz_states.state_decompress) {
return false;
}
assert(payload != buffer); assert(payload != buffer);
size_t data_length = qlz_decompress((char*) payload, (char*) buffer, qlz_states.state_decompress); size_t data_length = qlz_decompress((char*) payload, (char*) buffer, qlz_states.state_decompress);
if(data_length <= 0) if(data_length <= 0) {
return false; return false;
}
/* test for overflow */ /* test for overflow */
if(data_length > *buffer_size) terminate(); if(data_length > *buffer_size) terminate();
@ -80,76 +52,29 @@ namespace ts::compression {
} }
size_t qlz_compressed_size(const void* payload, size_t payload_length) { size_t qlz_compressed_size(const void* payload, size_t payload_length) {
(void) payload;
assert(payload_length >= 9); assert(payload_length >= 9);
return payload_length + 400; // http://www.quicklz.com/manual.html return payload_length + 400; // http://www.quicklz.com/manual.html
} }
bool qlz_compress_payload(const void* payload, size_t payload_length, void* buffer, size_t* buffer_length) { bool qlz_compress_payload(const void* payload, size_t payload_length, void* buffer, size_t* buffer_length) {
if(!qlz_states.state_compress) return false; if(!qlz_states.state_compress) {
return false;
}
assert(payload != buffer); assert(payload != buffer);
assert(*buffer_length >= qlz_compressed_size(payload, payload_length)); assert(*buffer_length >= qlz_compressed_size(payload, payload_length));
size_t compressed_length = qlz_compress(payload, (char*) buffer, payload_length, qlz_states.state_compress); size_t compressed_length = qlz_compress(payload, (char*) buffer, payload_length, qlz_states.state_compress);
if(compressed_length > *buffer_length) terminate(); if(compressed_length > *buffer_length) {
terminate();
}
if(compressed_length <= 0) if(compressed_length <= 0) {
return false; return false;
}
*buffer_length = compressed_length; *buffer_length = compressed_length;
return true; return true;
} }
} }
bool CompressionHandler::compress(protocol::BasicPacket* packet, std::string &error) {
auto packet_payload = packet->data();
auto header_length = packet->length() - packet_payload.length();
size_t max_compressed_payload_size = compression::qlz_compressed_size(packet_payload.data_ptr(), packet_payload.length());
auto target_buffer = buffer::allocate_buffer(max_compressed_payload_size + header_length);
size_t compressed_size{max_compressed_payload_size};
if(!compression::qlz_compress_payload(packet_payload.data_ptr(), packet_payload.length(), &target_buffer[header_length], &compressed_size)) return false;
memcpy(target_buffer.data_ptr(), packet->buffer().data_ptr(), header_length);
packet->buffer(target_buffer.range(0, compressed_size + header_length));
return true;
}
bool CompressionHandler::decompress(protocol::BasicPacket* packet, std::string &error) {
auto expected_length = compression::qlz_decompressed_size(packet->data().data_ptr(), packet->data().length());
if(expected_length > this->max_packet_size){ //Max 16MB. (97% Compression!)
error = "Invalid packet size. (Calculated target length of " + to_string(expected_length) + ". Max length: " + to_string(this->max_packet_size) + ")";
return false;
} else if(expected_length == 0) {
error = "Failed to calculate decompressed packet length";
return false;
}
auto header_length = packet->header().length() + packet->mac().length();
auto buffer = buffer::allocate_buffer(expected_length + header_length);
size_t compressed_size{expected_length};
if(!compression::qlz_decompress_payload(packet->data().data_ptr(), &buffer[header_length], &compressed_size)) return false;
memcpy(buffer.data_ptr(), packet->buffer().data_ptr(), header_length);
packet->buffer(buffer.range(0, compressed_size + header_length));
return true;
}
bool CompressionHandler::progressPacketIn(protocol::BasicPacket* packet, std::string &error) {
if(packet->isCompressed()) {
if(!this->decompress(packet, error)) return false;
packet->setCompressed(false);
}
return true;
}
bool CompressionHandler::progressPacketOut(protocol::BasicPacket* packet, std::string& error) {
if(packet->has_flag(protocol::PacketFlag::Compressed) && !packet->isCompressed()) {
if(!this->compress(packet, error)) return false;
packet->setCompressed(true);
}
return true;
}
CompressionHandler::CompressionHandler() { }
CompressionHandler::~CompressionHandler() { }

View File

@ -2,28 +2,11 @@
#include "Packet.h" #include "Packet.h"
namespace ts { namespace ts::compression {
namespace compression {
/* Attention: These methods does not validate the data! */ /* Attention: These methods does not validate the data! */
size_t qlz_decompressed_size(const void* payload, size_t payload_length); size_t qlz_decompressed_size(const void* payload, size_t payload_length);
bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size); //Attention: payload & buffer must be differen! bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size); //Attention: payload & buffer must be different!
size_t qlz_compressed_size(const void* payload, size_t payload_length); size_t qlz_compressed_size(const void* payload, size_t payload_length);
bool qlz_compress_payload(const void* payload, size_t payload_length, void* buffer, size_t* buffer_length); bool qlz_compress_payload(const void* payload, size_t payload_length, void* buffer, size_t* buffer_length);
}
namespace connection {
class CompressionHandler {
public:
CompressionHandler();
virtual ~CompressionHandler();
bool progressPacketOut(protocol::BasicPacket*, std::string&);
bool progressPacketIn(protocol::BasicPacket*, std::string&);
size_t max_packet_size = 16 * 1024;
private:
bool compress(protocol::BasicPacket*, std::string &error);
bool decompress(protocol::BasicPacket*, std::string &error);
};
}
} }

View File

@ -18,6 +18,8 @@ using namespace ts::protocol;
CryptHandler::CryptHandler() { CryptHandler::CryptHandler() {
memtrack::allocated<CryptHandler>(this); memtrack::allocated<CryptHandler>(this);
this->cipher_code = find_cipher("rijndael");
assert(this->cipher_code >= 0);
this->reset(); this->reset();
} }
@ -26,23 +28,26 @@ CryptHandler::~CryptHandler() {
} }
void CryptHandler::reset() { void CryptHandler::reset() {
this->useDefaultChipherKeyNonce = true; this->encryption_initialized_ = true;
this->iv_struct_length = 0; this->iv_struct_length = 0;
memset(this->iv_struct, 0, sizeof(this->iv_struct)); memset(this->iv_struct, 0, sizeof(this->iv_struct));
memcpy(this->current_mac, CryptHandler::default_mac, sizeof(CryptHandler::default_mac)); memcpy(this->current_mac, CryptHandler::default_mac, sizeof(CryptHandler::default_mac));
for(auto& cache : this->cache_key_client) for(auto& cache : this->cache_key_client) {
cache.generation = 0xFFEF; cache.generation = 0xFFEF;
for(auto& cache : this->cache_key_server) }
for(auto& cache : this->cache_key_server) {
cache.generation = 0xFFEF; cache.generation = 0xFFEF;
}
} }
#define SHARED_KEY_BUFFER_LENGTH (256) #define SHARED_KEY_BUFFER_LENGTH (256)
bool CryptHandler::setupSharedSecret(const std::string& alpha, const std::string& beta, ecc_key *publicKey, ecc_key *ownKey, std::string &error) { bool CryptHandler::setupSharedSecret(const std::string& alpha, const std::string& beta, ecc_key *remote_public_key, ecc_key *own_private_key, std::string &error) {
size_t buffer_length = SHARED_KEY_BUFFER_LENGTH; size_t buffer_length = SHARED_KEY_BUFFER_LENGTH;
uint8_t buffer[SHARED_KEY_BUFFER_LENGTH]; uint8_t buffer[SHARED_KEY_BUFFER_LENGTH];
int err; int err;
if((err = ecc_shared_secret(ownKey, publicKey, buffer, (unsigned long*) &buffer_length)) != CRYPT_OK){ if((err = ecc_shared_secret(own_private_key, remote_public_key, buffer, (unsigned long*) &buffer_length)) != CRYPT_OK){
error = "Could not calculate shared secret. Message: " + string(error_to_string(err)); error = "Could not calculate shared secret. Message: " + string(error_to_string(err));
return false; return false;
} }
@ -72,13 +77,13 @@ bool CryptHandler::setupSharedSecret(const std::string& alpha, const std::string
digest::sha1((const char*) iv_buffer, SHA_DIGEST_LENGTH, mac_buffer); digest::sha1((const char*) iv_buffer, SHA_DIGEST_LENGTH, mac_buffer);
memcpy(this->current_mac, mac_buffer, 8); memcpy(this->current_mac, mac_buffer, 8);
this->useDefaultChipherKeyNonce = false; this->encryption_initialized_ = false;
} }
return true; return true;
} }
void _fe_neg(fe h, const fe f) { void fe_neg_(fe h, const fe f) {
int32_t f0 = f[0]; int32_t f0 = f[0];
int32_t f1 = f[1]; int32_t f1 = f[1];
int32_t f2 = f[2]; int32_t f2 = f[2];
@ -118,8 +123,8 @@ inline void keyMul(uint8_t(& target_buffer)[32], const uint8_t* publicKey /* com
ge_frombytes_negate_vartime(&keyA, publicKey); ge_frombytes_negate_vartime(&keyA, publicKey);
if(negate) { if(negate) {
_fe_neg(*(fe*) &keyA.X, *(const fe*) &keyA.X); /* undo negate */ fe_neg_(*(fe*) &keyA.X, *(const fe*) &keyA.X); /* undo negate */
_fe_neg(*(fe*) &keyA.T, *(const fe*) &keyA.T); /* undo negate */ fe_neg_(*(fe*) &keyA.T, *(const fe*) &keyA.T); /* undo negate */
} }
ge_scalarmult_vartime(&result, privateKey, &keyA); ge_scalarmult_vartime(&result, privateKey, &keyA);
@ -149,7 +154,7 @@ bool CryptHandler::setupSharedSecretNew(const std::string &alpha, const std::str
uint8_t mac_buffer[SHA_DIGEST_LENGTH]; uint8_t mac_buffer[SHA_DIGEST_LENGTH];
digest::sha1((char*) this->iv_struct, 64, mac_buffer); digest::sha1((char*) this->iv_struct, 64, mac_buffer);
memcpy(this->current_mac, mac_buffer, 8); memcpy(this->current_mac, mac_buffer, 8);
this->useDefaultChipherKeyNonce = false; this->encryption_initialized_ = false;
} }
return true; return true;
@ -185,7 +190,8 @@ bool CryptHandler::generate_key_nonce(
} else { } else {
buffer[0] = 0x30; buffer[0] = 0x30;
} }
buffer[1] = (char) (type & 0xF);
buffer[1] = (char) (type & 0xFU);
le2be32(generation, buffer, 2); le2be32(generation, buffer, 2);
memcpy(&buffer[6], this->iv_struct, this->iv_struct_length); memcpy(&buffer[6], this->iv_struct, this->iv_struct_length);
@ -199,8 +205,8 @@ bool CryptHandler::generate_key_nonce(
} }
//Xor the key //Xor the key
key[0] ^= (uint8_t) ((packet_id >> 8) & 0xFFU); key[0] ^= (uint8_t) (packet_id >> 8U);
key[1] ^=(packet_id & 0xFFU); key[1] ^= (uint8_t) packet_id;
return true; return true;
} }
@ -211,8 +217,9 @@ bool CryptHandler::verify_encryption(const pipes::buffer_view &packet, uint16_t
key_t key{}; key_t key{};
nonce_t nonce{}; nonce_t nonce{};
if(!generate_key_nonce(true, (protocol::PacketType) (packet[12] & 0xF), packet_id, generation, key, nonce)) if(!generate_key_nonce(true, (protocol::PacketType) ((uint8_t) packet[12] & 0xFU), packet_id, generation, key, nonce)) {
return false; return false;
}
auto mac = packet.view(0, 8); auto mac = packet.view(0, 8);
auto header = packet.view(8, 5); auto header = packet.view(8, 5);
@ -220,14 +227,13 @@ bool CryptHandler::verify_encryption(const pipes::buffer_view &packet, uint16_t
auto length = data.length(); auto length = data.length();
/* static shareable void buffer */ const static unsigned long void_target_length{2048};
const static unsigned long void_target_length = 2048; uint8_t void_target_buffer[2048];
static uint8_t void_target_buffer[2048]; if(void_target_length < length) {
if(void_target_length < length)
return false; return false;
}
//TODO: Cache find_cipher err = eax_decrypt_verify_memory(this->cipher_code,
err = eax_decrypt_verify_memory(find_cipher("rijndael"),
(uint8_t *) key.data(), /* the key */ (uint8_t *) key.data(), /* the key */
(size_t) key.size(), /* key is 16 bytes */ (size_t) key.size(), /* key is 16 bytes */
(uint8_t *) nonce.data(), /* the nonce */ (uint8_t *) nonce.data(), /* the nonce */
@ -245,18 +251,16 @@ bool CryptHandler::verify_encryption(const pipes::buffer_view &packet, uint16_t
return err == CRYPT_OK && success; return err == CRYPT_OK && success;
} }
#define tmp_buffer_size (2048) bool CryptHandler::decrypt(const void *header, size_t header_length, void *payload, size_t payload_length, const void *mac, const key_t &key, const nonce_t &nonce, std::string &error) const {
bool CryptHandler::decrypt(const void *header, size_t header_length, void *payload, size_t payload_length, const void *mac, const key_t &key, const nonce_t &nonce, std::string &error) { const static unsigned long kTempBufferLength{2048};
if(tmp_buffer_size < payload_length) { uint8_t tmp_buffer[kTempBufferLength];
error = "buffer too large"; if(kTempBufferLength < payload_length) {
error = "packet too large";
return false; return false;
} }
uint8_t tmp_buffer[tmp_buffer_size];
int success; int success;
auto err = eax_decrypt_verify_memory(this->cipher_code,
//TODO: Cache cipher
auto err = eax_decrypt_verify_memory(find_cipher("rijndael"),
(const uint8_t *) key.data(), /* the key */ (const uint8_t *) key.data(), /* the key */
(unsigned long) key.size(), /* key is 16 bytes */ (unsigned long) key.size(), /* key is 16 bytes */
(const uint8_t *) nonce.data(), /* the nonce */ (const uint8_t *) nonce.data(), /* the nonce */
@ -289,17 +293,11 @@ bool CryptHandler::encrypt(
void *payload, size_t payload_length, void *payload, size_t payload_length,
void *mac, void *mac,
const key_t &key, const nonce_t &nonce, std::string &error) { const key_t &key, const nonce_t &nonce, std::string &error) {
if(tmp_buffer_size < payload_length) {
error = "buffer too large";
return false;
}
uint8_t tmp_buffer[tmp_buffer_size];
size_t tag_length{8}; size_t tag_length{8};
uint8_t tag_buffer[16]; uint8_t tag_buffer[16];
static_assert(sizeof(unsigned long) <= sizeof(tag_length)); static_assert(sizeof(unsigned long) <= sizeof(tag_length));
auto err = eax_encrypt_authenticate_memory(find_cipher("rijndael"), auto err = eax_encrypt_authenticate_memory(this->cipher_code,
(uint8_t *) key.data(), /* the key */ (uint8_t *) key.data(), /* the key */
(unsigned long) key.size(), /* key is 16 bytes */ (unsigned long) key.size(), /* key is 16 bytes */
(uint8_t *) nonce.data(), /* the nonce */ (uint8_t *) nonce.data(), /* the nonce */
@ -308,7 +306,7 @@ bool CryptHandler::encrypt(
(unsigned long) header_length, /* header length */ (unsigned long) header_length, /* header length */
(uint8_t *) payload, /* The plain text */ (uint8_t *) payload, /* The plain text */
(unsigned long) payload_length, /* Plain text length */ (unsigned long) payload_length, /* Plain text length */
(uint8_t *) tmp_buffer, /* The result buffer */ (uint8_t *) payload, /* The result buffer */
(uint8_t *) tag_buffer, (uint8_t *) tag_buffer,
(unsigned long *) &tag_length (unsigned long *) &tag_length
); );
@ -320,6 +318,5 @@ bool CryptHandler::encrypt(
} }
memcpy(mac, tag_buffer, 8); memcpy(mac, tag_buffer, 8);
memcpy(payload, tmp_buffer, payload_length);
return true; return true;
} }

View File

@ -8,10 +8,49 @@
namespace ts::connection { namespace ts::connection {
class CryptHandler { class CryptHandler {
enum Methode { public:
TEAMSPEAK_3_1, typedef std::array<uint8_t, 16> key_t;
TEAMSPEAK_3 typedef std::array<uint8_t, 16> nonce_t;
}; CryptHandler();
~CryptHandler();
void reset();
//TeamSpeak old
bool setupSharedSecret(const std::string& /* alpha */, const std::string& /* beta */, ecc_key* /* remote_public_key */, ecc_key* /* own_private_key */, std::string &/* error */);
bool setupSharedSecret(const std::string& /* alpha */, const std::string& /* beta */, const std::string& /* shared_key */, std::string &/* error */);
//TeamSpeak new
bool setupSharedSecretNew(const std::string& alpha, const std::string& beta, const char privateKey[32], const char publicKey[32]);
bool encrypt(
const void* /* header */, size_t /* header length */,
void* /* payload */, size_t /* payload length */,
void* /* mac */, /* mac must be 8 bytes long! */
const key_t& /* key */, const nonce_t& /* nonce */,
std::string& /* error */);
bool decrypt(
const void* /* header */, size_t /* header length */,
void* /* payload */, size_t /* payload length */,
const void* /* mac */, /* mac must be 8 bytes long! */
const key_t& /* key */, const nonce_t& /* nonce */,
std::string& /* error */) const;
bool generate_key_nonce(bool /* to server */, uint8_t /* packet type */, uint16_t /* packet id */, uint16_t /* generation */, key_t& /* key */, nonce_t& /* nonce */);
bool verify_encryption(const pipes::buffer_view& /* data */, uint16_t /* packet id */, uint16_t /* generation */);
inline void write_default_mac(void* buffer) {
memcpy(buffer, this->current_mac, 8);
}
[[nodiscard]] inline bool encryption_initialized() const { return !this->encryption_initialized_; }
static constexpr key_t kDefaultKey{'c', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e'}; //c:\windows\syste
static constexpr nonce_t kDefaultNonce{'m', '\\', 'f', 'i', 'r', 'e', 'w', 'a', 'l', 'l', '3', '2', '.', 'c', 'p', 'l'}; //m\firewall32.cpl
private:
static constexpr char default_mac[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'}; //TS3INIT1
struct KeyCache { struct KeyCache {
uint16_t generation = 0xFFEF; uint16_t generation = 0xFFEF;
union { union {
@ -22,67 +61,19 @@ namespace ts::connection {
uint8_t key_nonce[32]; uint8_t key_nonce[32];
}; };
}; };
public:
typedef std::array<uint8_t, 16> key_t;
typedef std::array<uint8_t, 16> nonce_t;
CryptHandler();
~CryptHandler();
void reset(); bool encryption_initialized_{false};
int cipher_code{-1};
//TeamSpeak old
bool setupSharedSecret(const std::string& alpha, const std::string& beta, ecc_key* publicKey, ecc_key* ownKey, std::string &error);
bool setupSharedSecret(const std::string& alpha, const std::string& beta, const std::string& sharedKey, std::string &error);
//TeamSpeak new
bool setupSharedSecretNew(const std::string& alpha, const std::string& beta, const char privateKey[32], const char publicKey[32]);
/* mac must be 8 bytes long! */
bool encrypt(
const void* /* header */, size_t /* header length */,
void* /* payload */, size_t /* payload length */,
void* /* mac */,
const key_t& /* key */, const nonce_t& /* nonce */,
std::string& /* error */);
/* mac must be 8 bytes long! */
bool decrypt(
const void* /* header */, size_t /* header length */,
void* /* payload */, size_t /* payload length */,
const void* /* mac */,
const key_t& /* key */, const nonce_t& /* nonce */,
std::string& /* error */);
bool generate_key_nonce(bool /* to server */, uint8_t /* packet type */, uint16_t /* packet id */, uint16_t /* generation */, key_t& /* key */, nonce_t& /* nonce */);
bool verify_encryption(const pipes::buffer_view& data, uint16_t packet_id, uint16_t generation);
inline void write_default_mac(void* buffer) {
memcpy(buffer, this->current_mac, 8);
}
[[nodiscard]] inline bool encryption_initialized() const { return !this->useDefaultChipherKeyNonce; }
static constexpr key_t kDefaultKey{'c', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e'}; //c:\windows\syste
static constexpr nonce_t kDefaultNonce{'m', '\\', 'f', 'i', 'r', 'e', 'w', 'a', 'l', 'l', '3', '2', '.', 'c', 'p', 'l'}; //m\firewall32.cpl
private:
static constexpr char default_mac[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'}; //TS3INIT1
bool generate_key_nonce(protocol::BasicPacket* packet, bool use_default, uint8_t(&)[16] /* key */, uint8_t(&)[16] /* nonce */);
//The default key and nonce
bool useDefaultChipherKeyNonce = true;
/* for the old protocol SHA1 length for the new 64 bytes */ /* for the old protocol SHA1 length for the new 64 bytes */
uint8_t iv_struct[64]; uint8_t iv_struct[64];
uint8_t iv_struct_length = 0; uint8_t iv_struct_length{0};
uint8_t current_mac[8]; uint8_t current_mac[8]{};
std::mutex cache_key_lock; std::mutex cache_key_lock{};
std::array<KeyCache, protocol::PACKET_MAX> cache_key_client; std::array<KeyCache, protocol::PACKET_MAX> cache_key_client{};
std::array<KeyCache, protocol::PACKET_MAX> cache_key_server; std::array<KeyCache, protocol::PACKET_MAX> cache_key_server{};
static_assert(sizeof(current_mac) == sizeof(default_mac), "invalid mac"); static_assert(sizeof(current_mac) == sizeof(default_mac), "invalid mac");
static_assert(sizeof(iv_struct) == 64, "invalid iv struct"); static_assert(sizeof(iv_struct) == 64, "invalid iv struct");

View File

@ -3,236 +3,14 @@
// //
#include <cstring> #include <cstring>
#include <iostream>
#include <bitset> #include <bitset>
#include <src/misc/spin_mutex.h> #include "./Packet.h"
#include "Packet.h" #include "../misc/endianness.h"
#include "buffers.h" #include "../misc/spin_mutex.h"
#include "misc/endianness.h"
using namespace std; using namespace std;
namespace ts { namespace ts {
namespace protocol { namespace protocol {
PacketTypeInfo::PacketTypeInfo(const std::string& name, PacketType type, bool ack, int max_length) noexcept {
this->data = new PacketTypeProperties{name, type, max_length, ack};
this->owns_data = true;
if(type < 0x0F)
types.insert({type, *this});
}
PacketTypeInfo::~PacketTypeInfo() {
if(this->owns_data)
delete this->data;
}
PacketTypeInfo::PacketTypeInfo(const PacketTypeInfo &red) : data(red.data) { }
std::map<int, PacketTypeInfo> PacketTypeInfo::types;
PacketTypeInfo PacketTypeInfo::fromid(int id) {
for(const auto& elm : types)
if(elm.first == id) return elm.second;
return PacketTypeInfo::Undefined;
}
PacketTypeInfo PacketTypeInfo::Voice = {"Voice", PacketType::VOICE, false, 1024};
PacketTypeInfo PacketTypeInfo::VoiceWhisper = {"VoiceWhisper", PacketType::VOICE_WHISPER, false, 1024};
PacketTypeInfo PacketTypeInfo::Command = {"Command", PacketType::COMMAND, true, 487};
PacketTypeInfo PacketTypeInfo::CommandLow = {"CommandLow", PacketType::COMMAND_LOW, true, 487};
PacketTypeInfo PacketTypeInfo::Ping = {"Ping", PacketType::PING, false, 1024};
PacketTypeInfo PacketTypeInfo::Pong = {"Pong", PacketType::PONG, false, 1024};
PacketTypeInfo PacketTypeInfo::Ack = {"Ack", PacketType::ACK, false, 1024};
PacketTypeInfo PacketTypeInfo::AckLow = {"AckLow", PacketType::ACK_LOW, false, 1024};
PacketTypeInfo PacketTypeInfo::Init1 = {"Init1", PacketType::INIT1, false, 1024};
PacketTypeInfo PacketTypeInfo::Undefined = {"Undefined", PacketType::UNDEFINED, false, 1024};
namespace PacketFlag {
std::string to_string(PacketFlag flag){
switch(flag){
case Fragmented:
return "Fragmented";
case NewProtocol:
return "NewProtocol";
case Compressed:
return "Compressed";
case Unencrypted:
return "Unencrypted";
default:
return "None";
}
}
}
BasicPacket::BasicPacket(size_t header_length, size_t data_length) {
this->_header_length = (uint8_t) header_length;
this->_buffer = pipes::buffer(MAC_SIZE + this->_header_length + data_length);
memset(this->_buffer.data_ptr(), 0, this->_buffer.length());
}
BasicPacket::~BasicPacket() {}
void BasicPacket::append_data(const std::vector<pipes::buffer> &data) {
size_t length = 0;
for(const auto& buffer : data)
length += buffer.length();
/* we've to allocate a new buffer because out buffer is fixed in size */
size_t index = this->_buffer.length();
auto new_buffer = buffer::allocate_buffer(length + index);
new_buffer.write(this->_buffer, index);
for(const auto& buffer : data) {
new_buffer.write(buffer, buffer.length(), index);
index += buffer.length();
}
this->_buffer = new_buffer;
}
std::string BasicPacket::flags() const {
std::string result;
if(this->has_flag(PacketFlag::Unencrypted)) result += string(result.empty() ? "" : " | ") + "Unencrypted";
if(this->has_flag(PacketFlag::Compressed)) result += string(result.empty() ? "" : " | ") + "Compressed";
if(this->has_flag(PacketFlag::Fragmented)) result += string(result.empty() ? "" : " | ") + "Fragmented";
if(this->has_flag(PacketFlag::NewProtocol)) result += string(result.empty() ? "" : " | ") + "NewProtocol";
if(result.empty()) result = "none";
return result;
}
void BasicPacket::applyPacketId(PacketIdManager& manager) {
this->applyPacketId(manager.nextPacketId(this->type()), manager.generationId(this->type()));
}
void BasicPacket::applyPacketId(uint16_t packetId, uint16_t generationId) {
if(this->memory_state.id_branded)
throw std::logic_error("Packet already got a packet id!");
this->memory_state.id_branded = true;
this->setPacketId(packetId, generationId);
}
/**
* @param buffer -> [mac][Header [uint16 BE packetId | [uint8](4bit flags | 4bit type)]][Data]
* @return
*/
std::unique_ptr<ServerPacket> ServerPacket::from_buffer(const pipes::buffer_view &buffer) {
auto result = make_unique<ServerPacket>();
result->_buffer = buffer.own_buffer();
result->_header_length = SERVER_HEADER_SIZE;
return result;
}
ServerPacket::ServerPacket(uint8_t flagMask, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) {
this->header()[2] = flagMask;
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ServerPacket::ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) {
this->header()[2] |= (uint8_t) type.type();
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ServerPacket::ServerPacket(ts::protocol::PacketTypeInfo type, size_t data_length) : BasicPacket(SERVER_HEADER_SIZE, data_length) {
this->header()[2] |= type.type();
}
ServerPacket::~ServerPacket() {}
uint16_t ServerPacket::packetId() const {
return be2le16(&this->header()[0]);
}
void ServerPacket::setPacketId(uint16_t pkId, uint16_t gen) {
le2be16(pkId, &this->header()[0]);
this->genId = gen;
}
uint16_t ServerPacket::generationId() const {
return this->genId;
}
PacketTypeInfo ServerPacket::type() const {
return PacketTypeInfo::fromid(this->header()[2] & 0xF);
}
std::unique_ptr<ClientPacket> ClientPacket::from_buffer(const pipes::buffer_view &buffer) {
auto result = make_unique<ClientPacket>();
result->_buffer = buffer.own_buffer();
result->_header_length = CLIENT_HEADER_SIZE;
return result;
}
ClientPacket::ClientPacket(const PacketTypeInfo &type, const pipes::buffer_view& data) : BasicPacket(CLIENT_HEADER_SIZE, data.length()) {
this->header()[4] = type.type() & 0xF;
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ClientPacket::ClientPacket(const PacketTypeInfo &type, uint8_t flag_mask, const pipes::buffer_view& data) : ClientPacket(type, data) {
this->header()[4] |= flag_mask;
}
ClientPacket::~ClientPacket() {}
uint16_t ClientPacket::packetId() const {
return be2le16(&this->header()[0]);
}
uint16_t ClientPacket::generationId() const {
return this->genId;
}
PacketTypeInfo ClientPacket::type() const {
return PacketTypeInfo::fromid(this->header()[4] & 0xF);
}
void ClientPacket::type(const ts::protocol::PacketTypeInfo &type) {
auto& field = this->header().data_ptr<uint8_t>()[4];
field &= (uint8_t) ~0xF;
field |= type.type();
}
void ClientPacket::setPacketId(uint16_t pkId, uint16_t gen) {
this->header()[0] = (uint8_t) ((pkId >> 8) & 0xFF);
this->header()[1] = (uint8_t) ((pkId >> 0) & 0xFF);
this->genId = gen;
}
uint16_t ClientPacket::clientId() const {
return be2le16(&this->header()[2]);
}
void ClientPacket::clientId(uint16_t clId) {
this->header()[2] = clId >> 8;
this->header()[3] = clId & 0xFF;
}
/* New packet parser API */
bool PacketParser::is_encrypted() const {
if(this->decrypted) return false;
return (this->flags() & PacketFlag::Unencrypted) == 0;
}
bool PacketParser::is_compressed() const {
if(this->uncompressed) return false;
return (this->flags() & PacketFlag::Compressed) > 0;
}
bool PacketParser::is_fragmented() const {
if(this->defragmented) return false;
return (this->flags() & PacketFlag::Fragmented) > 0;
}
uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr<uint8_t>(), ClientPacketParser::kHeaderOffset + 0); } uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr<uint8_t>(), ClientPacketParser::kHeaderOffset + 0); }
uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr<uint8_t>(), ClientPacketParser::kHeaderOffset + 2); } uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr<uint8_t>(), ClientPacketParser::kHeaderOffset + 2); }
uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; } uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; }
@ -361,7 +139,6 @@ namespace ts {
} }
#else #else
void protocol::OutgoingServerPacket::object_freed() { void protocol::OutgoingServerPacket::object_freed() {
//TODO: Bukkit list?
deconstruct_osp(this); deconstruct_osp(this);
::free(this); ::free(this);
} }

View File

@ -8,8 +8,7 @@
#include <pipes/buffer.h> #include <pipes/buffer.h>
#include "../query/Command.h" #include "../query/Command.h"
namespace ts { namespace ts::protocol {
namespace protocol {
enum PacketType : uint8_t { enum PacketType : uint8_t {
VOICE = 0x00, VOICE = 0x00,
VOICE_WHISPER = 0x01, VOICE_WHISPER = 0x01,
@ -25,91 +24,38 @@ namespace ts {
UNDEFINED = 0xFF UNDEFINED = 0xFF
}; };
struct PacketTypeProperties {
std::string name;
PacketType type;
int max_length;
bool requireAcknowledge;
};
class PacketTypeInfo {
public:
static PacketTypeInfo Voice;
static PacketTypeInfo VoiceWhisper;
static PacketTypeInfo Command;
static PacketTypeInfo CommandLow;
static PacketTypeInfo Ping;
static PacketTypeInfo Pong;
static PacketTypeInfo Ack;
static PacketTypeInfo AckLow;
static PacketTypeInfo Init1;
static PacketTypeInfo Undefined;
static PacketTypeInfo fromid(int id);
std::string name() const { return data->name; }
PacketType type() const { return data->type; }
bool requireAcknowledge(){ return data->requireAcknowledge; }
bool operator==(const PacketTypeInfo& other) const {
return other.data->type == this->data->type;
}
bool operator!=(const PacketTypeInfo& other){
return other.data->type != this->data->type;
}
int max_length() const { return data->max_length; }
inline bool fragmentable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; }
inline bool compressable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; }
PacketTypeInfo(const PacketTypeInfo&);
PacketTypeInfo(PacketTypeInfo&& remote) : data(remote.data) {}
~PacketTypeInfo();
private:
static std::map<int, PacketTypeInfo> types;
PacketTypeInfo(const std::string&, PacketType, bool, int) noexcept;
PacketTypeProperties* data;
bool owns_data = false;
};
class PacketIdManager { class PacketIdManager {
public: public:
PacketIdManager() { PacketIdManager() = default;
this->reset();
}
~PacketIdManager() = default; ~PacketIdManager() = default;
PacketIdManager(const PacketIdManager& ref) = delete; PacketIdManager(const PacketIdManager& ref) = delete;
PacketIdManager(PacketIdManager&& ref) = delete; PacketIdManager(PacketIdManager&& ref) = delete;
[[nodiscard]] uint16_t nextPacketId(const PacketTypeInfo &type){ [[nodiscard]] uint16_t nextPacketId(const PacketType &type) {
return static_cast<uint16_t>(this->packetCounter[type.type()]++ & 0xFFFF); return (uint16_t) (this->packet_counter[(uint8_t) type & 0xFU]++);
} }
[[nodiscard]] uint16_t currentPacketId(const PacketTypeInfo &type){ [[nodiscard]] uint16_t currentPacketId(const PacketType &type) {
return static_cast<uint16_t>(this->packetCounter[type.type()] & 0xFFFF); return (uint16_t) (this->packet_counter[(uint8_t) type & 0xFU]);
} }
[[nodiscard]] uint16_t generationId(const PacketTypeInfo &type){ [[nodiscard]] uint16_t generationId(const PacketType &type) {
return static_cast<uint16_t>((this->packetCounter[type.type()] >> 16) & 0xFFFF); return (uint16_t) (this->packet_counter[(uint8_t) type & 0xFU] >> 16U);
} }
[[nodiscard]] uint32_t generate_full_id(const PacketType& type) { [[nodiscard]] uint32_t generate_full_id(const PacketType& type) {
return this->packetCounter[type]++; return this->packet_counter[type]++;
} }
void reset() { void reset() {
memset(&this->packetCounter[0], 0, sizeof(uint32_t) * 16); memset(&this->packet_counter[0], 0, sizeof(uint32_t) * 16);
} }
private: private:
uint32_t packetCounter[16]{}; std::array<uint32_t, 16> packet_counter{};
}; };
namespace PacketFlag { enum struct PacketFlag {
enum PacketFlag : uint8_t {
None = 0x00, None = 0x00,
Fragmented = 0x10, //If packet type voice then its toggle the CELT Mono Fragmented = 0x10, //If packet type voice then its toggle the CELT Mono
NewProtocol = 0x20, NewProtocol = 0x20,
@ -118,174 +64,30 @@ namespace ts {
}; };
typedef uint8_t PacketFlags; typedef uint8_t PacketFlags;
std::string to_string(PacketFlag flag); constexpr const char* packet_flag_to_string(const PacketFlag& flag) {
switch(flag){
case PacketFlag::Fragmented:
return "Fragmented";
case PacketFlag::NewProtocol:
return "NewProtocol";
case PacketFlag::Compressed:
return "Compressed";
case PacketFlag::Unencrypted:
return "Unencrypted";
case PacketFlag::None:
default:
return "None";
}
} }
#define MAC_SIZE 8 #define MAC_SIZE 8
#define SERVER_HEADER_SIZE 3 #define SERVER_HEADER_SIZE 3
#define CLIENT_HEADER_SIZE 5 #define CLIENT_HEADER_SIZE 5
class BasicPacket {
public:
explicit BasicPacket(size_t header_length, size_t data_length);
virtual ~BasicPacket();
BasicPacket(const BasicPacket&) = delete;
BasicPacket(BasicPacket&&) = delete;
virtual uint16_t packetId() const = 0;
virtual uint16_t generationId() const = 0;
virtual PacketTypeInfo type() const = 0;
/* packet flag info */
inline bool has_flag(PacketFlag::PacketFlag flag) const { return this->_flags_type_byte() & flag; }
inline uint8_t flag_mask() const { return this->_flags_type_byte(); };
[[nodiscard]] std::string flags() const;
/* manipulate flags */
inline void set_flags(PacketFlag::PacketFlags flags) {
uint8_t& byte = this->_flags_type_byte();
byte &= 0xF; /* clear all flags */
byte |= (flags & 0xF0);
}
inline void enable_flag(PacketFlag::PacketFlag flag){ this->toggle_flag(flag, true); }
inline void toggle_flag(PacketFlag::PacketFlag flag, bool state) {
if(state)
this->_flags_type_byte() |= flag;
else
this->_flags_type_byte() &= (uint8_t) ~flag;
}
virtual void applyPacketId(PacketIdManager &);
virtual void applyPacketId(uint16_t, uint16_t);
void setListener(std::unique_ptr<threads::Future<bool>> listener){
if(!this->type().requireAcknowledge())
throw std::logic_error("Packet type does not support a acknowledge listener!");
this->listener = std::move(listener);
}
inline std::unique_ptr<threads::Future<bool>>& getListener() { return this->listener; }
inline size_t length() const { return this->_buffer.length(); }
inline const pipes::buffer_view mac() const { return this->_buffer.view(0, MAC_SIZE); }
inline pipes::buffer mac() { return this->_buffer.range(0, MAC_SIZE); }
inline size_t mac_length() const { return MAC_SIZE; }
inline const pipes::buffer_view header() const { return this->_buffer.view(MAC_SIZE, this->_header_length); }
inline pipes::buffer header() { return this->_buffer.range(MAC_SIZE, this->_header_length); }
inline size_t header_length() const { return this->_header_length; }
inline size_t data_length() const { return this->_buffer.length() - (MAC_SIZE + this->_header_length); }
inline const pipes::buffer_view data() const { return this->_buffer.view(MAC_SIZE + this->_header_length); }
inline pipes::buffer data() { return this->_buffer.range(MAC_SIZE + this->_header_length); }
void append_data(const std::vector<pipes::buffer> &data);
inline void data(const pipes::buffer_view &data){
this->_buffer.resize(MAC_SIZE + this->_header_length + data.length());
memcpy((char*) this->_buffer.data_ptr() + MAC_SIZE + this->_header_length, data.data_ptr(), data.length());
}
inline void mac(const pipes::buffer_view &_new){
assert(_new.length() >= MAC_SIZE);
memcpy(this->_buffer.data_ptr(), _new.data_ptr(), MAC_SIZE);
}
[[nodiscard]] inline bool isEncrypted() const { return this->memory_state.encrypted; }
inline void setEncrypted(bool flag){ this->memory_state.encrypted = flag; }
[[nodiscard]] inline bool isCompressed() const { return this->memory_state.compressed; }
inline void setCompressed(bool flag){ this->memory_state.compressed = flag; }
[[nodiscard]] inline bool isFragmentEntry() const { return this->memory_state.fragment_entry; }
inline void setFragmentedEntry(bool flag){ this->memory_state.fragment_entry = flag; }
Command asCommand();
//Has the size of a byte
union {
#ifdef WIN32
__pragma(pack(push, 1))
#endif
struct {
bool encrypted: 1;
bool compressed: 1;
bool fragment_entry: 1;
bool id_branded: 1;
}
#ifdef WIN32
__pragma(pack(pop));
#else
__attribute__((packed));
#endif
uint8_t flags = 0;
} memory_state;
pipes::buffer buffer() { return this->_buffer; }
void buffer(pipes::buffer buffer) {
assert(buffer.length() >= this->_header_length + MAC_SIZE);
this->_buffer = std::move(buffer);
}
protected:
BasicPacket() = default;
virtual const uint8_t& _flags_type_byte() const = 0;
virtual uint8_t& _flags_type_byte() = 0;
virtual void setPacketId(uint16_t, uint16_t) = 0;
uint8_t _header_length;
pipes::buffer _buffer;
uint16_t genId = 0;
std::unique_ptr<threads::Future<bool>> listener;
};
/**
* Packet from the client
*/
class ClientPacket : public BasicPacket {
friend std::unique_ptr<ClientPacket> std::make_unique<ClientPacket>();
public:
static constexpr size_t META_MAC_SIZE = 8;
static constexpr size_t META_HEADER_SIZE = CLIENT_HEADER_SIZE;
static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE;
[[nodiscard]] static std::unique_ptr<ClientPacket> from_buffer(const pipes::buffer_view& buffer);
ClientPacket(const PacketTypeInfo& type, const pipes::buffer_view& data);
ClientPacket(const PacketTypeInfo& type, uint8_t flag_mask, const pipes::buffer_view& data);
~ClientPacket() override;
ClientPacket(const ClientPacket&) = delete;
ClientPacket(ClientPacket&&) = delete;
uint16_t clientId() const;
void clientId(uint16_t);
uint16_t packetId() const override;
uint16_t generationId() const override;
void generationId(uint16_t generation) { this->genId = generation; }
PacketTypeInfo type() const override;
void type(const PacketTypeInfo&);
private:
ClientPacket() = default;
const uint8_t &_flags_type_byte() const override {
return this->header().data_ptr<uint8_t>()[4];
}
uint8_t &_flags_type_byte() override {
return this->header().data_ptr<uint8_t>()[4];
}
void setPacketId(uint16_t, uint16_t) override;
};
class PacketParser { class PacketParser {
public: public:
PacketParser(const PacketParser&) = delete; PacketParser(const PacketParser&) = delete;
@ -299,14 +101,23 @@ namespace ts {
[[nodiscard]] virtual pipes::buffer_view payload() const = 0; [[nodiscard]] virtual pipes::buffer_view payload() const = 0;
[[nodiscard]] virtual size_t payload_length() const = 0; [[nodiscard]] virtual size_t payload_length() const = 0;
[[nodiscard]] inline uint32_t full_packet_id() const { return this->packet_id() | (this->estimated_generation() << 16U); } [[nodiscard]] inline uint32_t full_packet_id() const { return this->packet_id() | (uint32_t) ((uint32_t) this->estimated_generation() << 16U); }
[[nodiscard]] virtual uint16_t packet_id() const = 0; [[nodiscard]] virtual uint16_t packet_id() const = 0;
[[nodiscard]] virtual uint8_t type() const = 0; [[nodiscard]] virtual uint8_t type() const = 0;
[[nodiscard]] virtual uint8_t flags() const = 0; [[nodiscard]] virtual uint8_t flags() const = 0;
[[nodiscard]] bool is_encrypted() const; [[nodiscard]] inline bool has_flag(const PacketFlag& flag) const { return this->flags() & (uint8_t) flag; }
[[nodiscard]] bool is_compressed() const; [[nodiscard]] inline bool is_encrypted() const {
[[nodiscard]] bool is_fragmented() const; return !this->decrypted && !this->has_flag(PacketFlag::Unencrypted);
}
[[nodiscard]] inline bool is_compressed() const {
return !this->uncompressed && this->has_flag(PacketFlag::Compressed);
}
[[nodiscard]] inline bool is_fragmented() const {
return !this->defragmented && this->has_flag(PacketFlag::Fragmented);
}
[[nodiscard]] uint16_t estimated_generation() const { return this->generation; } [[nodiscard]] uint16_t estimated_generation() const { return this->generation; }
void set_estimated_generation(uint16_t gen) { this->generation = gen; } void set_estimated_generation(uint16_t gen) { this->generation = gen; }
@ -344,55 +155,13 @@ namespace ts {
[[nodiscard]] uint8_t flags() const override; [[nodiscard]] uint8_t flags() const override;
}; };
/** class ServerPacketParser : public PacketParser {
* Packet from the server
*/
class ServerPacket : public BasicPacket {
friend std::unique_ptr<ServerPacket> std::make_unique<ServerPacket>();
public:
static constexpr size_t META_MAC_SIZE = 8;
static constexpr size_t META_HEADER_SIZE = SERVER_HEADER_SIZE;
static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE;
[[nodiscard]] static std::unique_ptr<ServerPacket> from_buffer(const pipes::buffer_view& buffer);
ServerPacket(uint8_t flagMask, const pipes::buffer_view& data);
ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data);
ServerPacket(PacketTypeInfo type, size_t /* data length */);
~ServerPacket() override;
ServerPacket(const ServerPacket&) = delete;
ServerPacket(ServerPacket&&) = delete;
[[nodiscard]] uint16_t packetId() const override;
[[nodiscard]] uint16_t generationId() const override;
void generationId(uint16_t generation) { this->genId = generation; }
[[nodiscard]] PacketTypeInfo type() const override;
private:
ServerPacket() = default;
[[nodiscard]] const uint8_t &_flags_type_byte() const override {
return this->header().data_ptr<uint8_t>()[2];
}
uint8_t &_flags_type_byte() override {
return this->header().data_ptr<uint8_t>()[2];
}
void setPacketId(uint16_t, uint16_t) override;
};
class ServerPacketP {
public: public:
constexpr static auto kHeaderOffset = 8; constexpr static auto kHeaderOffset = 8;
constexpr static auto kHeaderLength = SERVER_HEADER_SIZE; constexpr static auto kHeaderLength = SERVER_HEADER_SIZE;
constexpr static auto kPayloadOffset = kHeaderOffset + SERVER_HEADER_SIZE; constexpr static auto kPayloadOffset = kHeaderOffset + SERVER_HEADER_SIZE;
};
class ServerPacketParser : public PacketParser, public ServerPacketP {
public:
explicit ServerPacketParser(pipes::buffer_view buffer) : PacketParser{std::move(buffer)} {} explicit ServerPacketParser(pipes::buffer_view buffer) : PacketParser{std::move(buffer)} {}
ServerPacketParser(const ServerPacketParser&) = delete; ServerPacketParser(const ServerPacketParser&) = delete;
@ -462,5 +231,26 @@ namespace ts {
/* This will allocate a new outgoing packet. To delete just unref the packet! */ /* This will allocate a new outgoing packet. To delete just unref the packet! */
OutgoingServerPacket* allocate_outgoing_packet(size_t /* payload size */); OutgoingServerPacket* allocate_outgoing_packet(size_t /* payload size */);
inline PacketFlags& operator|=(PacketFlags& flags, const PacketFlag& flag) {
flags |= (uint8_t) flag;
return flags;
}
inline PacketFlags operator|(PacketFlags flags, const PacketFlag& flag) {
return flags |= flag;
}
inline PacketFlags& operator&=(PacketFlags& flags, const PacketFlag& flag) {
flags &= (uint8_t) flag;
return flags;
}
inline PacketFlags operator&(PacketFlags flags, const PacketFlag& flag) {
return flags &= flag;
}
inline PacketFlags operator|(const PacketFlag& flag_a, const PacketFlag& flag_b) {
return (uint8_t) flag_a | (uint8_t) flag_b;
} }
} }

View File

@ -2,22 +2,22 @@
using namespace ts::protocol; using namespace ts::protocol;
generation_estimator::generation_estimator() { GenerationEstimator::GenerationEstimator() {
this->reset(); this->reset();
} }
void generation_estimator::reset() { void GenerationEstimator::reset() {
this->last_generation = 0; this->last_generation = 0;
this->last_packet_id = 0; this->last_packet_id = 0;
} }
uint16_t generation_estimator::visit_packet(uint16_t packet_id) { uint16_t GenerationEstimator::visit_packet(uint16_t packet_id) {
if(this->last_packet_id >= generation_estimator::overflow_area_begin) { if(this->last_packet_id >= GenerationEstimator::overflow_area_begin) {
if(packet_id > this->last_packet_id) { if(packet_id > this->last_packet_id) {
/* normal behaviour */ /* normal behaviour */
this->last_packet_id = packet_id; this->last_packet_id = packet_id;
return this->last_generation; return this->last_generation;
} else if(packet_id < generation_estimator::overflow_area_end) { } else if(packet_id < GenerationEstimator::overflow_area_end) {
/* we're within a new generation */ /* we're within a new generation */
this->last_packet_id = packet_id; this->last_packet_id = packet_id;
return ++this->last_generation; return ++this->last_generation;
@ -25,8 +25,8 @@ uint16_t generation_estimator::visit_packet(uint16_t packet_id) {
/* old packet */ /* old packet */
return this->last_generation; return this->last_generation;
} }
} else if(this->last_packet_id <= generation_estimator::overflow_area_end) { } else if(this->last_packet_id <= GenerationEstimator::overflow_area_end) {
if(packet_id >= generation_estimator::overflow_area_begin) /* old packet */ if(packet_id >= GenerationEstimator::overflow_area_begin) /* old packet */
return this->last_generation - 1; return this->last_generation - 1;
if(packet_id > this->last_packet_id) if(packet_id > this->last_packet_id)
this->last_packet_id = packet_id; this->last_packet_id = packet_id;

View File

@ -3,9 +3,9 @@
#include <cstdint> #include <cstdint>
namespace ts::protocol { namespace ts::protocol {
class generation_estimator { class GenerationEstimator {
public: public:
generation_estimator(); GenerationEstimator();
void reset(); void reset();
[[nodiscard]] uint16_t visit_packet(uint16_t /* packet id */); [[nodiscard]] uint16_t visit_packet(uint16_t /* packet id */);

View File

@ -9,7 +9,7 @@
// 1.5.0 final // 1.5.0 final
#include "QuickLZ.h" #include "./QuickLZ.h"
#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0 #if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0
#error quicklz.c and quicklz.h have different versions #error quicklz.c and quicklz.h have different versions
@ -19,8 +19,829 @@
#define X86X64 #define X86X64
#endif #endif
#define MINOFFSET 2
#define UNCONDITIONAL_MATCHLEN 6
#define UNCOMPRESSED_END 4
#define CWORD_LEN 4
#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
#define OFFSET_BASE source
#define CAST (ui32)(size_t)
#else
#define OFFSET_BASE 0
#define CAST
#endif
int qlz_get_setting(int setting)
{
switch (setting)
{
case 0: return QLZ_COMPRESSION_LEVEL;
case 1: return sizeof(qlz_state_compress);
case 2: return sizeof(qlz_state_decompress);
case 3: return QLZ_STREAMING_BUFFER;
#ifdef QLZ_MEMORY_SAFE
case 6: return 1;
#else
case 6: return 0;
#endif
case 7: return QLZ_VERSION_MAJOR;
case 8: return QLZ_VERSION_MINOR;
case 9: return QLZ_VERSION_REVISION;
}
return -1;
}
#if QLZ_COMPRESSION_LEVEL == 1
static int same(const unsigned char *src, size_t n)
{
while(n > 0 && *(src + n) == *src)
n--;
return n == 0 ? 1 : 0;
}
#endif
static void reset_table_compress(qlz_state_compress *state)
{
int i;
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
#if QLZ_COMPRESSION_LEVEL == 1
state->hash[i].offset = 0;
#else
state->hash_counter[i] = 0;
#endif
}
}
static void reset_table_decompress(qlz_state_decompress *state)
{
int i;
(void)state;
(void)i;
#if QLZ_COMPRESSION_LEVEL == 2
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
state->hash_counter[i] = 0;
}
#endif
}
static __inline ui32 hash_func(ui32 i)
{
#if QLZ_COMPRESSION_LEVEL == 2
return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1);
#else
return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1);
#endif
}
static __inline ui32 fast_read(void const *src, ui32 bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)src;
switch (bytes)
{
case 4:
return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24);
case 3:
return(*p | *(p + 1) << 8 | *(p + 2) << 16);
case 2:
return(*p | *(p + 1) << 8);
case 1:
return(*p);
}
return 0;
#else
if (bytes >= 1 && bytes <= 4)
return *((ui32*)src);
else
return 0;
#endif
}
static __inline ui32 hashat(const unsigned char *src)
{
ui32 fetch, hash;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
return hash;
}
static __inline void fast_write(ui32 f, void *dst, size_t bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)dst;
switch (bytes)
{
case 4:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
*(p + 3) = (unsigned char)(f >> 24);
return;
case 3:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
return;
case 2:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
return;
case 1:
*p = (unsigned char)f;
return;
}
#else
switch (bytes)
{
case 4:
*((ui32*)dst) = f;
return;
case 3:
*((ui32*)dst) = f;
return;
case 2:
*((ui16 *)dst) = (ui16)f;
return;
case 1:
*((unsigned char*)dst) = (unsigned char)f;
return;
}
#endif
}
size_t qlz_size_decompressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1 + n, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_compressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_header(const char *source) size_t qlz_size_header(const char *source)
{ {
size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1; size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1;
return n; return n;
} }
static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n)
{
// Caution if modifying memcpy_up! Overlap of dst and src must be special handled.
#ifndef X86X64
unsigned char *end = dst + n;
while(dst < end)
{
*dst = *src;
dst++;
src++;
}
#else
ui32 f = 0;
do
{
*(ui32 *)(dst + f) = *(ui32 *)(src + f);
f += MINOFFSET + 1;
}
while (f < n);
#endif
}
static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
hash = hashat(s);
state->hash[hash].offset = s;
state->hash_counter[hash] = 1;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(s);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s;
c++;
state->hash_counter[hash] = c;
#endif
(void)state;
(void)s;
}
#if QLZ_COMPRESSION_LEVEL <= 2
static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max)
{
while(*lh < max)
{
(*lh)++;
update_hash(state, *lh);
}
}
#endif
static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state)
{
const unsigned char *last_byte = source + size - 1;
const unsigned char *src = source;
unsigned char *cword_ptr = destination;
unsigned char *dst = destination + CWORD_LEN;
ui32 cword_val = 1U << 31;
const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
ui32 fetch = 0;
unsigned int lits = 0;
(void) lits;
if(src <= last_matchstart)
fetch = fast_read(src, 3);
while(src <= last_matchstart)
{
if ((cword_val & 1) == 1)
{
// store uncompressed if compression ratio is too low
if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5))
return 0;
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
fetch = fast_read(src, 3);
}
#if QLZ_COMPRESSION_LEVEL == 1
{
const unsigned char *o;
ui32 hash, cached;
hash = hash_func(fetch);
cached = fetch ^ state->hash[hash].cache;
state->hash[hash].cache = fetch;
o = state->hash[hash].offset + OFFSET_BASE;
state->hash[hash].offset = CAST(src - OFFSET_BASE);
#ifdef X86X64
if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if(cached != 0)
{
#else
if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if (*(o + 3) != *(src + 3))
{
#endif
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
fast_write((3 - 2) | hash, dst, 2);
src += 3;
dst += 2;
}
else
{
const unsigned char *old_src = src;
size_t matchlen;
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
src += 4;
if(*(o + (src - old_src)) == *src)
{
src++;
if(*(o + (src - old_src)) == *src)
{
size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1;
size_t remaining = q > 255 ? 255 : q;
src++;
while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining)
src++;
}
}
matchlen = src - old_src;
if (matchlen < 18)
{
fast_write((ui32)(matchlen - 2) | hash, dst, 2);
dst += 2;
}
else
{
fast_write((ui32)(matchlen << 16) | hash, dst, 3);
dst += 3;
}
}
fetch = fast_read(src, 3);
lits = 0;
}
else
{
lits++;
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
#ifdef X86X64
fetch = fast_read(src, 3);
#else
fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16);
#endif
}
}
#elif QLZ_COMPRESSION_LEVEL >= 2
{
const unsigned char *o, *offset2;
ui32 hash, matchlen, k, m, best_k = 0;
unsigned char c;
size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1);
(void)best_k;
//hash = hashat(src);
fetch = fast_read(src, 3);
hash = hash_func(fetch);
c = state->hash_counter[hash];
offset2 = state->hash[hash].offset[0];
if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0)
{
matchlen = 3;
if(*(offset2 + matchlen) == *(src + matchlen))
{
matchlen = 4;
while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining)
matchlen++;
}
}
else
matchlen = 0;
for(k = 1; k < QLZ_POINTERS && c > k; k++)
{
o = state->hash[hash].offset[k];
#if QLZ_COMPRESSION_LEVEL == 3
if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#elif QLZ_COMPRESSION_LEVEL == 2
if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#endif
{
m = 3;
while(*(o + m) == *(src + m) && m < remaining)
m++;
#if QLZ_COMPRESSION_LEVEL == 3
if ((m > matchlen) || (m == matchlen && o > offset2))
#elif QLZ_COMPRESSION_LEVEL == 2
if (m > matchlen)
#endif
{
offset2 = o;
matchlen = m;
best_k = k;
}
}
}
o = offset2;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#if QLZ_COMPRESSION_LEVEL == 3
if(matchlen > 2 && src - o < 131071)
{
ui32 u;
size_t offset = src - o;
for(u = 1; u < matchlen; u++)
{
hash = hashat(src + u);
c = state->hash_counter[hash]++;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u;
}
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if(matchlen == 3 && offset <= 63)
{
*dst = (unsigned char)(offset << 2);
dst++;
}
else if (matchlen == 3 && offset <= 16383)
{
ui32 f = (ui32)((offset << 2) | 1);
fast_write(f, dst, 2);
dst += 2;
}
else if (matchlen <= 18 && offset <= 1023)
{
ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2;
fast_write(f, dst, 2);
dst += 2;
}
else if(matchlen <= 33)
{
ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3;
fast_write(f, dst, 3);
dst += 3;
}
else
{
ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3;
fast_write(f, dst, 4);
dst += 4;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#elif QLZ_COMPRESSION_LEVEL == 2
if(matchlen > 2)
{
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if (matchlen < 10)
{
ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5);
fast_write(f, dst, 2);
dst += 2;
}
else
{
ui32 f = best_k | (matchlen << 16) | (hash << 5);
fast_write(f, dst, 3);
dst += 3;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#endif
}
#endif
}
while (src <= last_byte)
{
if ((cword_val & 1) == 1)
{
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
}
#if QLZ_COMPRESSION_LEVEL < 3
if (src <= last_byte - 3)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash, fetch;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
state->hash[hash].offset = CAST(src - OFFSET_BASE);
state->hash[hash].cache = fetch;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(src);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#endif
}
#endif
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
while((cword_val & 1) != 1)
cword_val = (cword_val >> 1);
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
// min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument
return dst - destination < 9 ? 9 : dst - destination;
}
static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history)
{
const unsigned char *src = source + qlz_size_header((const char *)source);
unsigned char *dst = destination;
const unsigned char *last_destination_byte = destination + size - 1;
ui32 cword_val = 1;
const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
unsigned char *last_hashed = destination - 1;
const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1;
static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
(void) last_source_byte;
(void) last_hashed;
(void) state;
(void) history;
for(;;)
{
ui32 fetch;
if (cword_val == 1)
{
#ifdef QLZ_MEMORY_SAFE
if(src + CWORD_LEN - 1 > last_source_byte)
return 0;
#endif
cword_val = fast_read(src, CWORD_LEN);
src += CWORD_LEN;
}
#ifdef QLZ_MEMORY_SAFE
if(src + 4 - 1 > last_source_byte)
return 0;
#endif
fetch = fast_read(src, 4);
if ((cword_val & 1) == 1)
{
ui32 matchlen;
const unsigned char *offset2;
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
cword_val = cword_val >> 1;
hash = (fetch >> 4) & 0xfff;
offset2 = (const unsigned char *)(size_t)state->hash[hash].offset;
if((fetch & 0xf) != 0)
{
matchlen = (fetch & 0xf) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
cword_val = cword_val >> 1;
hash = (fetch >> 5) & 0x7ff;
c = (unsigned char)(fetch & 0x3);
offset2 = state->hash[hash].offset[c];
if((fetch & (28)) != 0)
{
matchlen = ((fetch >> 2) & 0x7) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 3
ui32 offset;
cword_val = cword_val >> 1;
if ((fetch & 3) == 0)
{
offset = (fetch & 0xff) >> 2;
matchlen = 3;
src++;
}
else if ((fetch & 2) == 0)
{
offset = (fetch & 0xffff) >> 2;
matchlen = 3;
src += 2;
}
else if ((fetch & 1) == 0)
{
offset = (fetch & 0xffff) >> 6;
matchlen = ((fetch >> 2) & 15) + 3;
src += 2;
}
else if ((fetch & 127) != 3)
{
offset = (fetch >> 7) & 0x1ffff;
matchlen = ((fetch >> 2) & 0x1f) + 2;
src += 3;
}
else
{
offset = (fetch >> 15);
matchlen = ((fetch >> 7) & 255) + 3;
src += 4;
}
offset2 = dst - offset;
#endif
#ifdef QLZ_MEMORY_SAFE
if(offset2 < history || offset2 > dst - MINOFFSET - 1)
return 0;
if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1))
return 0;
#endif
memcpy_up(dst, offset2, matchlen);
dst += matchlen;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - matchlen);
last_hashed = dst - 1;
#endif
}
else
{
if (dst < last_matchstart)
{
unsigned int n = bitlut[cword_val & 0xf];
#ifdef X86X64
*(ui32 *)dst = *(ui32 *)src;
#else
memcpy_up(dst, src, 4);
#endif
cword_val = cword_val >> n;
dst += n;
src += n;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - 3);
#endif
}
else
{
while(dst <= last_destination_byte)
{
if (cword_val == 1)
{
src += CWORD_LEN;
cword_val = 1U << 31;
}
#ifdef QLZ_MEMORY_SAFE
if(src >= last_source_byte + 1)
return 0;
#endif
*dst = *src;
dst++;
src++;
cword_val = cword_val >> 1;
}
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant
#endif
return size;
}
}
}
}
size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state)
{
size_t r;
ui32 compressed;
size_t base;
if(size == 0 || size > 0xffffffff - 400)
return 0;
if(size < 216)
base = 3;
else
base = 9;
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
reset_table_compress(state);
r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state);
#if QLZ_STREAMING_BUFFER > 0
reset_table_compress(state);
#endif
if(r == base)
{
memcpy(destination + base, source, size);
r = size + base;
compressed = 0;
}
else
{
compressed = 1;
}
state->stream_counter = 0;
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *src = state->stream_buffer + state->stream_counter;
memcpy(src, source, size);
r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state);
if(r == base)
{
memcpy(destination + base, src, size);
r = size + base;
compressed = 0;
reset_table_compress(state);
}
else
{
compressed = 1;
}
state->stream_counter += size;
}
#endif
if(base == 3)
{
*destination = (unsigned char)(0 | compressed);
*(destination + 1) = (unsigned char)r;
*(destination + 2) = (unsigned char)size;
}
else
{
*destination = (unsigned char)(2 | compressed);
fast_write((ui32)r, destination + 1, 4);
fast_write((ui32)size, destination + 5, 4);
}
*destination |= (QLZ_COMPRESSION_LEVEL << 2);
*destination |= (1 << 6);
*destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4);
// 76543210
// 01SSLLHC
return r;
}
size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state)
{
size_t dsiz = qlz_size_decompressed(source);
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
if((*source & 1) == 1)
{
reset_table_decompress(state);
dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination);
}
else
{
memcpy(destination, source + qlz_size_header(source), dsiz);
}
state->stream_counter = 0;
reset_table_decompress(state);
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *dst = state->stream_buffer + state->stream_counter;
if((*source & 1) == 1)
{
dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer);
}
else
{
memcpy(dst, source + qlz_size_header(source), dsiz);
reset_table_decompress(state);
}
memcpy(destination, dst, dsiz);
state->stream_counter += dsiz;
}
#endif
return dsiz;
}

View File

@ -18,25 +18,10 @@
// 1.5.0 final // 1.5.0 final
#ifndef QLZ_COMPRESSION_LEVEL /* Fixed definitions for TeaSpeak. */
// 1 gives fastest compression speed. 3 gives fastest decompression speed and best #define QLZ_COMPRESSION_LEVEL 1
// compression ratio.
#define QLZ_COMPRESSION_LEVEL 3
//#define QLZ_COMPRESSION_LEVEL 2
//#define QLZ_COMPRESSION_LEVEL 3
#endif
#ifndef QLZ_STREAMING_BUFFER
// If > 0, zero out both states prior to first call to qlz_compress() or qlz_decompress()
// and decompress packets in the same order as they were compressed
#define QLZ_STREAMING_BUFFER 0 #define QLZ_STREAMING_BUFFER 0
//#define QLZ_STREAMING_BUFFER 100000 #define QLZ_MEMORY_SAFE
//#define QLZ_STREAMING_BUFFER 1000000
// Guarantees that decompression of corrupted data cannot crash. Decreases decompression
// speed 10-20%. Compression speed not affected.
//#define QLZ_MEMORY_SAFE
#endif
#define QLZ_VERSION_MAJOR 1 #define QLZ_VERSION_MAJOR 1
#define QLZ_VERSION_MINOR 5 #define QLZ_VERSION_MINOR 5
@ -67,11 +52,12 @@ typedef unsigned short int ui16;
// Detect if pointer size is 64-bit. It's not fatal if some 64-bit target is not detected because this is only for adding an optional 64-bit optimization. // Detect if pointer size is 64-bit. It's not fatal if some 64-bit target is not detected because this is only for adding an optional 64-bit optimization.
#if defined _LP64 || defined __LP64__ || defined __64BIT__ || _ADDR64 || defined _WIN64 || defined __arch64__ || __WORDSIZE == 64 || (defined __sparc && defined __sparcv9) || defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 || defined __ia64 || defined __IA64__ #if defined _LP64 || defined __LP64__ || defined __64BIT__ || _ADDR64 || defined _WIN64 || defined __arch64__ || __WORDSIZE == 64 || (defined __sparc && defined __sparcv9) || defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 || defined __ia64 || defined __IA64__
#define QLZ_PTR_64 #define QLZ_PTR_64
#endif #endif
// hash entry // hash entry
typedef struct { typedef struct
{
#if QLZ_COMPRESSION_LEVEL == 1 #if QLZ_COMPRESSION_LEVEL == 1
ui32 cache; ui32 cache;
#if defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 #if defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
@ -85,7 +71,8 @@ typedef struct {
} qlz_hash_compress; } qlz_hash_compress;
typedef struct { typedef struct
{
#if QLZ_COMPRESSION_LEVEL == 1 #if QLZ_COMPRESSION_LEVEL == 1
const unsigned char *offset; const unsigned char *offset;
#else #else
@ -95,10 +82,11 @@ typedef struct {
// states // states
typedef struct { typedef struct
#if QLZ_STREAMING_BUFFER > 0 {
#if QLZ_STREAMING_BUFFER > 0
unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; unsigned char stream_buffer[QLZ_STREAMING_BUFFER];
#endif #endif
size_t stream_counter; size_t stream_counter;
qlz_hash_compress hash[QLZ_HASH_VALUES]; qlz_hash_compress hash[QLZ_HASH_VALUES];
unsigned char hash_counter[QLZ_HASH_VALUES]; unsigned char hash_counter[QLZ_HASH_VALUES];
@ -106,16 +94,17 @@ typedef struct {
#if QLZ_COMPRESSION_LEVEL == 1 || QLZ_COMPRESSION_LEVEL == 2 #if QLZ_COMPRESSION_LEVEL == 1 || QLZ_COMPRESSION_LEVEL == 2
typedef struct { typedef struct
{
#if QLZ_STREAMING_BUFFER > 0 #if QLZ_STREAMING_BUFFER > 0
unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; unsigned char stream_buffer[QLZ_STREAMING_BUFFER];
#endif #endif
qlz_hash_decompress hash[QLZ_HASH_VALUES]; qlz_hash_decompress hash[QLZ_HASH_VALUES];
unsigned char hash_counter[QLZ_HASH_VALUES]; unsigned char hash_counter[QLZ_HASH_VALUES];
size_t stream_counter; size_t stream_counter;
} qlz_state_decompress; } qlz_state_decompress;
#elif QLZ_COMPRESSION_LEVEL == 3 #elif QLZ_COMPRESSION_LEVEL == 3
typedef struct typedef struct
{ {
#if QLZ_STREAMING_BUFFER > 0 #if QLZ_STREAMING_BUFFER > 0
unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; unsigned char stream_buffer[QLZ_STREAMING_BUFFER];
@ -132,22 +121,6 @@ typedef struct
extern "C" { extern "C" {
#endif #endif
#if QLZ_COMPRESSION_LEVEL == 1
#define qlz_size_decompressed qlz_size_decompressed_level1
#define qlz_size_compressed qlz_size_compressed_level1
#define qlz_compress qlz_compress_level1
#define qlz_decompress qlz_decompress_level1
#define qlz_get_setting qlz_get_setting_level1
#elif QLZ_COMPRESSION_LEVEL == 2
#define DEFINE_QLZ_FN(type, name, ...) type name##_level2(#__VA_ARGS__);
#elif QLZ_COMPRESSION_LEVEL == 3
#define qlz_size_decompressed qlz_size_decompressed_level3
#define qlz_size_compressed qlz_size_compressed_level3
#define qlz_compress qlz_compress_level3
#define qlz_decompress qlz_decompress_level3
#define qlz_get_setting qlz_get_setting_level3
#endif
// Public functions of QuickLZ // Public functions of QuickLZ
size_t qlz_size_decompressed(const char *source); size_t qlz_size_decompressed(const char *source);
size_t qlz_size_compressed(const char *source); size_t qlz_size_compressed(const char *source);

View File

@ -1,843 +0,0 @@
// Fast data compression library
// Copyright (C) 2006-2011 Lasse Mikkel Reinhold
// lar@quicklz.com
//
// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything
// released into public must be open source) or under a commercial license if such
// has been acquired (see http://www.quicklz.com/order.html). The commercial license
// does not cover derived or ported versions created by third parties under GPL.
// 1.5.0 final
#define QLZ_COMPRESSION_LEVEL 1
#define QLZ_MEMORY_SAFE
#include "QuickLZ.h"
#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0
#error quicklz.c and quicklz.h have different versions
#endif
#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64))
#define X86X64
#endif
#define MINOFFSET 2
#define UNCONDITIONAL_MATCHLEN 6
#define UNCOMPRESSED_END 4
#define CWORD_LEN 4
#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
#define OFFSET_BASE source
#define CAST (ui32)(size_t)
#else
#define OFFSET_BASE 0
#define CAST
#endif
int qlz_get_setting(int setting)
{
switch (setting)
{
case 0: return QLZ_COMPRESSION_LEVEL;
case 1: return sizeof(qlz_state_compress);
case 2: return sizeof(qlz_state_decompress);
case 3: return QLZ_STREAMING_BUFFER;
#ifdef QLZ_MEMORY_SAFE
case 6: return 1;
#else
case 6: return 0;
#endif
case 7: return QLZ_VERSION_MAJOR;
case 8: return QLZ_VERSION_MINOR;
case 9: return QLZ_VERSION_REVISION;
}
return -1;
}
#if QLZ_COMPRESSION_LEVEL == 1
static int same(const unsigned char *src, size_t n)
{
while(n > 0 && *(src + n) == *src)
n--;
return n == 0 ? 1 : 0;
}
#endif
static void reset_table_compress(qlz_state_compress *state)
{
int i;
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
#if QLZ_COMPRESSION_LEVEL == 1
state->hash[i].offset = 0;
#else
state->hash_counter[i] = 0;
#endif
}
}
static void reset_table_decompress(qlz_state_decompress *state)
{
int i;
(void)state;
(void)i;
#if QLZ_COMPRESSION_LEVEL == 2
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
state->hash_counter[i] = 0;
}
#endif
}
static __inline ui32 hash_func(ui32 i)
{
#if QLZ_COMPRESSION_LEVEL == 2
return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1);
#else
return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1);
#endif
}
static __inline ui32 fast_read(void const *src, ui32 bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)src;
switch (bytes)
{
case 4:
return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24);
case 3:
return(*p | *(p + 1) << 8 | *(p + 2) << 16);
case 2:
return(*p | *(p + 1) << 8);
case 1:
return(*p);
}
return 0;
#else
if (bytes >= 1 && bytes <= 4)
return *((ui32*)src);
else
return 0;
#endif
}
static __inline ui32 hashat(const unsigned char *src)
{
ui32 fetch, hash;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
return hash;
}
static __inline void fast_write(ui32 f, void *dst, size_t bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)dst;
switch (bytes)
{
case 4:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
*(p + 3) = (unsigned char)(f >> 24);
return;
case 3:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
return;
case 2:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
return;
case 1:
*p = (unsigned char)f;
return;
}
#else
switch (bytes)
{
case 4:
*((ui32*)dst) = f;
return;
case 3:
*((ui32*)dst) = f;
return;
case 2:
*((ui16 *)dst) = (ui16)f;
return;
case 1:
*((unsigned char*)dst) = (unsigned char)f;
return;
}
#endif
}
size_t qlz_size_decompressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1 + n, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_compressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n)
{
// Caution if modifying memcpy_up! Overlap of dst and src must be special handled.
#ifndef X86X64
unsigned char *end = dst + n;
while(dst < end)
{
*dst = *src;
dst++;
src++;
}
#else
ui32 f = 0;
do
{
*(ui32 *)(dst + f) = *(ui32 *)(src + f);
f += MINOFFSET + 1;
}
while (f < n);
#endif
}
static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
hash = hashat(s);
state->hash[hash].offset = s;
state->hash_counter[hash] = 1;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(s);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s;
c++;
state->hash_counter[hash] = c;
#endif
(void)state;
(void)s;
}
#if QLZ_COMPRESSION_LEVEL <= 2
static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max)
{
while(*lh < max)
{
(*lh)++;
update_hash(state, *lh);
}
}
#endif
static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state)
{
const unsigned char *last_byte = source + size - 1;
const unsigned char *src = source;
unsigned char *cword_ptr = destination;
unsigned char *dst = destination + CWORD_LEN;
ui32 cword_val = 1U << 31;
const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
ui32 fetch = 0;
unsigned int lits = 0;
(void) lits;
if(src <= last_matchstart)
fetch = fast_read(src, 3);
while(src <= last_matchstart)
{
if ((cword_val & 1) == 1)
{
// store uncompressed if compression ratio is too low
if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5))
return 0;
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
fetch = fast_read(src, 3);
}
#if QLZ_COMPRESSION_LEVEL == 1
{
const unsigned char *o;
ui32 hash, cached;
hash = hash_func(fetch);
cached = fetch ^ state->hash[hash].cache;
state->hash[hash].cache = fetch;
o = state->hash[hash].offset + OFFSET_BASE;
state->hash[hash].offset = CAST(src - OFFSET_BASE);
#ifdef X86X64
if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if(cached != 0)
{
#else
if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if (*(o + 3) != *(src + 3))
{
#endif
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
fast_write((3 - 2) | hash, dst, 2);
src += 3;
dst += 2;
}
else
{
const unsigned char *old_src = src;
size_t matchlen;
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
src += 4;
if(*(o + (src - old_src)) == *src)
{
src++;
if(*(o + (src - old_src)) == *src)
{
size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1;
size_t remaining = q > 255 ? 255 : q;
src++;
while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining)
src++;
}
}
matchlen = src - old_src;
if (matchlen < 18)
{
fast_write((ui32)(matchlen - 2) | hash, dst, 2);
dst += 2;
}
else
{
fast_write((ui32)(matchlen << 16) | hash, dst, 3);
dst += 3;
}
}
fetch = fast_read(src, 3);
lits = 0;
}
else
{
lits++;
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
#ifdef X86X64
fetch = fast_read(src, 3);
#else
fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16);
#endif
}
}
#elif QLZ_COMPRESSION_LEVEL >= 2
{
const unsigned char *o, *offset2;
ui32 hash, matchlen, k, m, best_k = 0;
unsigned char c;
size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1);
(void)best_k;
//hash = hashat(src);
fetch = fast_read(src, 3);
hash = hash_func(fetch);
c = state->hash_counter[hash];
offset2 = state->hash[hash].offset[0];
if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0)
{
matchlen = 3;
if(*(offset2 + matchlen) == *(src + matchlen))
{
matchlen = 4;
while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining)
matchlen++;
}
}
else
matchlen = 0;
for(k = 1; k < QLZ_POINTERS && c > k; k++)
{
o = state->hash[hash].offset[k];
#if QLZ_COMPRESSION_LEVEL == 3
if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#elif QLZ_COMPRESSION_LEVEL == 2
if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#endif
{
m = 3;
while(*(o + m) == *(src + m) && m < remaining)
m++;
#if QLZ_COMPRESSION_LEVEL == 3
if ((m > matchlen) || (m == matchlen && o > offset2))
#elif QLZ_COMPRESSION_LEVEL == 2
if (m > matchlen)
#endif
{
offset2 = o;
matchlen = m;
best_k = k;
}
}
}
o = offset2;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#if QLZ_COMPRESSION_LEVEL == 3
if(matchlen > 2 && src - o < 131071)
{
ui32 u;
size_t offset = src - o;
for(u = 1; u < matchlen; u++)
{
hash = hashat(src + u);
c = state->hash_counter[hash]++;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u;
}
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if(matchlen == 3 && offset <= 63)
{
*dst = (unsigned char)(offset << 2);
dst++;
}
else if (matchlen == 3 && offset <= 16383)
{
ui32 f = (ui32)((offset << 2) | 1);
fast_write(f, dst, 2);
dst += 2;
}
else if (matchlen <= 18 && offset <= 1023)
{
ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2;
fast_write(f, dst, 2);
dst += 2;
}
else if(matchlen <= 33)
{
ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3;
fast_write(f, dst, 3);
dst += 3;
}
else
{
ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3;
fast_write(f, dst, 4);
dst += 4;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#elif QLZ_COMPRESSION_LEVEL == 2
if(matchlen > 2)
{
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if (matchlen < 10)
{
ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5);
fast_write(f, dst, 2);
dst += 2;
}
else
{
ui32 f = best_k | (matchlen << 16) | (hash << 5);
fast_write(f, dst, 3);
dst += 3;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#endif
}
#endif
}
while (src <= last_byte)
{
if ((cword_val & 1) == 1)
{
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
}
#if QLZ_COMPRESSION_LEVEL < 3
if (src <= last_byte - 3)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash, fetch;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
state->hash[hash].offset = CAST(src - OFFSET_BASE);
state->hash[hash].cache = fetch;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(src);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#endif
}
#endif
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
while((cword_val & 1) != 1)
cword_val = (cword_val >> 1);
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
// min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument
return dst - destination < 9 ? 9 : dst - destination;
}
extern size_t qlz_size_header(const char *source);
static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history)
{
const unsigned char *src = source + qlz_size_header((const char *)source);
unsigned char *dst = destination;
const unsigned char *last_destination_byte = destination + size - 1;
ui32 cword_val = 1;
const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
unsigned char *last_hashed = destination - 1;
const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1;
static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
(void) last_source_byte;
(void) last_hashed;
(void) state;
(void) history;
for(;;)
{
ui32 fetch;
if (cword_val == 1)
{
#ifdef QLZ_MEMORY_SAFE
if(src + CWORD_LEN - 1 > last_source_byte)
return 0;
#endif
cword_val = fast_read(src, CWORD_LEN);
src += CWORD_LEN;
}
#ifdef QLZ_MEMORY_SAFE
if(src + 4 - 1 > last_source_byte)
return 0;
#endif
fetch = fast_read(src, 4);
if ((cword_val & 1) == 1)
{
ui32 matchlen;
const unsigned char *offset2;
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
cword_val = cword_val >> 1;
hash = (fetch >> 4) & 0xfff;
offset2 = (const unsigned char *)(size_t)state->hash[hash].offset;
if((fetch & 0xf) != 0)
{
matchlen = (fetch & 0xf) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
cword_val = cword_val >> 1;
hash = (fetch >> 5) & 0x7ff;
c = (unsigned char)(fetch & 0x3);
offset2 = state->hash[hash].offset[c];
if((fetch & (28)) != 0)
{
matchlen = ((fetch >> 2) & 0x7) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 3
ui32 offset;
cword_val = cword_val >> 1;
if ((fetch & 3) == 0)
{
offset = (fetch & 0xff) >> 2;
matchlen = 3;
src++;
}
else if ((fetch & 2) == 0)
{
offset = (fetch & 0xffff) >> 2;
matchlen = 3;
src += 2;
}
else if ((fetch & 1) == 0)
{
offset = (fetch & 0xffff) >> 6;
matchlen = ((fetch >> 2) & 15) + 3;
src += 2;
}
else if ((fetch & 127) != 3)
{
offset = (fetch >> 7) & 0x1ffff;
matchlen = ((fetch >> 2) & 0x1f) + 2;
src += 3;
}
else
{
offset = (fetch >> 15);
matchlen = ((fetch >> 7) & 255) + 3;
src += 4;
}
offset2 = dst - offset;
#endif
#ifdef QLZ_MEMORY_SAFE
if(offset2 < history || offset2 > dst - MINOFFSET - 1)
return 0;
if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1))
return 0;
#endif
memcpy_up(dst, offset2, matchlen);
dst += matchlen;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - matchlen);
last_hashed = dst - 1;
#endif
}
else
{
if (dst < last_matchstart)
{
unsigned int n = bitlut[cword_val & 0xf];
#ifdef X86X64
*(ui32 *)dst = *(ui32 *)src;
#else
memcpy_up(dst, src, 4);
#endif
cword_val = cword_val >> n;
dst += n;
src += n;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - 3);
#endif
}
else
{
while(dst <= last_destination_byte)
{
if (cword_val == 1)
{
src += CWORD_LEN;
cword_val = 1U << 31;
}
#ifdef QLZ_MEMORY_SAFE
if(src >= last_source_byte + 1)
return 0;
#endif
*dst = *src;
dst++;
src++;
cword_val = cword_val >> 1;
}
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant
#endif
return size;
}
}
}
}
size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state)
{
size_t r;
ui32 compressed;
size_t base;
if(size == 0 || size > 0xffffffff - 400)
return 0;
if(size < 216)
base = 3;
else
base = 9;
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
reset_table_compress(state);
r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state);
#if QLZ_STREAMING_BUFFER > 0
reset_table_compress(state);
#endif
if(r == base)
{
memcpy(destination + base, source, size);
r = size + base;
compressed = 0;
}
else
{
compressed = 1;
}
state->stream_counter = 0;
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *src = state->stream_buffer + state->stream_counter;
memcpy(src, source, size);
r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state);
if(r == base)
{
memcpy(destination + base, src, size);
r = size + base;
compressed = 0;
reset_table_compress(state);
}
else
{
compressed = 1;
}
state->stream_counter += size;
}
#endif
if(base == 3)
{
*destination = (unsigned char)(0 | compressed);
*(destination + 1) = (unsigned char)r;
*(destination + 2) = (unsigned char)size;
}
else
{
*destination = (unsigned char)(2 | compressed);
fast_write((ui32)r, destination + 1, 4);
fast_write((ui32)size, destination + 5, 4);
}
*destination |= (QLZ_COMPRESSION_LEVEL << 2);
*destination |= (1 << 6);
*destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4);
// 76543210
// 01SSLLHC
return r;
}
size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state)
{
size_t dsiz = qlz_size_decompressed(source);
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
if((*source & 1) == 1)
{
reset_table_decompress(state);
dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination);
}
else
{
memcpy(destination, source + qlz_size_header(source), dsiz);
}
state->stream_counter = 0;
reset_table_decompress(state);
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *dst = state->stream_buffer + state->stream_counter;
if((*source & 1) == 1)
{
dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer);
}
else
{
memcpy(dst, source + qlz_size_header(source), dsiz);
reset_table_decompress(state);
}
memcpy(destination, dst, dsiz);
state->stream_counter += dsiz;
}
#endif
return dsiz;
}

View File

@ -1,848 +0,0 @@
// Fast data compression library
// Copyright (C) 2006-2011 Lasse Mikkel Reinhold
// lar@quicklz.com
//
// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything
// released into public must be open source) or under a commercial license if such
// has been acquired (see http://www.quicklz.com/order.html). The commercial license
// does not cover derived or ported versions created by third parties under GPL.
// 1.5.0 final
#define QLZ_COMPRESSION_LEVEL 2
#include "QuickLZ.h"
#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0
#error quicklz.c and quicklz.h have different versions
#endif
#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64))
#define X86X64
#endif
#define MINOFFSET 2
#define UNCONDITIONAL_MATCHLEN 6
#define UNCOMPRESSED_END 4
#define CWORD_LEN 4
#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
#define OFFSET_BASE source
#define CAST (ui32)(size_t)
#else
#define OFFSET_BASE 0
#define CAST
#endif
int qlz_get_setting(int setting)
{
switch (setting)
{
case 0: return QLZ_COMPRESSION_LEVEL;
case 1: return sizeof(qlz_state_compress);
case 2: return sizeof(qlz_state_decompress);
case 3: return QLZ_STREAMING_BUFFER;
#ifdef QLZ_MEMORY_SAFE
case 6: return 1;
#else
case 6: return 0;
#endif
case 7: return QLZ_VERSION_MAJOR;
case 8: return QLZ_VERSION_MINOR;
case 9: return QLZ_VERSION_REVISION;
}
return -1;
}
#if QLZ_COMPRESSION_LEVEL == 1
static int same(const unsigned char *src, size_t n)
{
while(n > 0 && *(src + n) == *src)
n--;
return n == 0 ? 1 : 0;
}
#endif
static void reset_table_compress(qlz_state_compress *state)
{
int i;
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
#if QLZ_COMPRESSION_LEVEL == 1
state->hash[i].offset = 0;
#else
state->hash_counter[i] = 0;
#endif
}
}
static void reset_table_decompress(qlz_state_decompress *state)
{
int i;
(void)state;
(void)i;
#if QLZ_COMPRESSION_LEVEL == 2
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
state->hash_counter[i] = 0;
}
#endif
}
static __inline ui32 hash_func(ui32 i)
{
#if QLZ_COMPRESSION_LEVEL == 2
return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1);
#else
return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1);
#endif
}
static __inline ui32 fast_read(void const *src, ui32 bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)src;
switch (bytes)
{
case 4:
return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24);
case 3:
return(*p | *(p + 1) << 8 | *(p + 2) << 16);
case 2:
return(*p | *(p + 1) << 8);
case 1:
return(*p);
}
return 0;
#else
if (bytes >= 1 && bytes <= 4)
return *((ui32*)src);
else
return 0;
#endif
}
static __inline ui32 hashat(const unsigned char *src)
{
ui32 fetch, hash;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
return hash;
}
static __inline void fast_write(ui32 f, void *dst, size_t bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)dst;
switch (bytes)
{
case 4:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
*(p + 3) = (unsigned char)(f >> 24);
return;
case 3:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
return;
case 2:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
return;
case 1:
*p = (unsigned char)f;
return;
}
#else
switch (bytes)
{
case 4:
*((ui32*)dst) = f;
return;
case 3:
*((ui32*)dst) = f;
return;
case 2:
*((ui16 *)dst) = (ui16)f;
return;
case 1:
*((unsigned char*)dst) = (unsigned char)f;
return;
}
#endif
}
size_t qlz_size_decompressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1 + n, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_compressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_header(const char *source)
{
size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1;
return n;
}
static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n)
{
// Caution if modifying memcpy_up! Overlap of dst and src must be special handled.
#ifndef X86X64
unsigned char *end = dst + n;
while(dst < end)
{
*dst = *src;
dst++;
src++;
}
#else
ui32 f = 0;
do
{
*(ui32 *)(dst + f) = *(ui32 *)(src + f);
f += MINOFFSET + 1;
}
while (f < n);
#endif
}
static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
hash = hashat(s);
state->hash[hash].offset = s;
state->hash_counter[hash] = 1;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(s);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s;
c++;
state->hash_counter[hash] = c;
#endif
(void)state;
(void)s;
}
#if QLZ_COMPRESSION_LEVEL <= 2
static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max)
{
while(*lh < max)
{
(*lh)++;
update_hash(state, *lh);
}
}
#endif
static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state)
{
const unsigned char *last_byte = source + size - 1;
const unsigned char *src = source;
unsigned char *cword_ptr = destination;
unsigned char *dst = destination + CWORD_LEN;
ui32 cword_val = 1U << 31;
const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
ui32 fetch = 0;
unsigned int lits = 0;
(void) lits;
if(src <= last_matchstart)
fetch = fast_read(src, 3);
while(src <= last_matchstart)
{
if ((cword_val & 1) == 1)
{
// store uncompressed if compression ratio is too low
if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5))
return 0;
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
fetch = fast_read(src, 3);
}
#if QLZ_COMPRESSION_LEVEL == 1
{
const unsigned char *o;
ui32 hash, cached;
hash = hash_func(fetch);
cached = fetch ^ state->hash[hash].cache;
state->hash[hash].cache = fetch;
o = state->hash[hash].offset + OFFSET_BASE;
state->hash[hash].offset = CAST(src - OFFSET_BASE);
#ifdef X86X64
if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if(cached != 0)
{
#else
if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if (*(o + 3) != *(src + 3))
{
#endif
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
fast_write((3 - 2) | hash, dst, 2);
src += 3;
dst += 2;
}
else
{
const unsigned char *old_src = src;
size_t matchlen;
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
src += 4;
if(*(o + (src - old_src)) == *src)
{
src++;
if(*(o + (src - old_src)) == *src)
{
size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1;
size_t remaining = q > 255 ? 255 : q;
src++;
while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining)
src++;
}
}
matchlen = src - old_src;
if (matchlen < 18)
{
fast_write((ui32)(matchlen - 2) | hash, dst, 2);
dst += 2;
}
else
{
fast_write((ui32)(matchlen << 16) | hash, dst, 3);
dst += 3;
}
}
fetch = fast_read(src, 3);
lits = 0;
}
else
{
lits++;
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
#ifdef X86X64
fetch = fast_read(src, 3);
#else
fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16);
#endif
}
}
#elif QLZ_COMPRESSION_LEVEL >= 2
{
const unsigned char *o, *offset2;
ui32 hash, matchlen, k, m, best_k = 0;
unsigned char c;
size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1);
(void)best_k;
//hash = hashat(src);
fetch = fast_read(src, 3);
hash = hash_func(fetch);
c = state->hash_counter[hash];
offset2 = state->hash[hash].offset[0];
if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0)
{
matchlen = 3;
if(*(offset2 + matchlen) == *(src + matchlen))
{
matchlen = 4;
while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining)
matchlen++;
}
}
else
matchlen = 0;
for(k = 1; k < QLZ_POINTERS && c > k; k++)
{
o = state->hash[hash].offset[k];
#if QLZ_COMPRESSION_LEVEL == 3
if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#elif QLZ_COMPRESSION_LEVEL == 2
if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#endif
{
m = 3;
while(*(o + m) == *(src + m) && m < remaining)
m++;
#if QLZ_COMPRESSION_LEVEL == 3
if ((m > matchlen) || (m == matchlen && o > offset2))
#elif QLZ_COMPRESSION_LEVEL == 2
if (m > matchlen)
#endif
{
offset2 = o;
matchlen = m;
best_k = k;
}
}
}
o = offset2;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#if QLZ_COMPRESSION_LEVEL == 3
if(matchlen > 2 && src - o < 131071)
{
ui32 u;
size_t offset = src - o;
for(u = 1; u < matchlen; u++)
{
hash = hashat(src + u);
c = state->hash_counter[hash]++;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u;
}
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if(matchlen == 3 && offset <= 63)
{
*dst = (unsigned char)(offset << 2);
dst++;
}
else if (matchlen == 3 && offset <= 16383)
{
ui32 f = (ui32)((offset << 2) | 1);
fast_write(f, dst, 2);
dst += 2;
}
else if (matchlen <= 18 && offset <= 1023)
{
ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2;
fast_write(f, dst, 2);
dst += 2;
}
else if(matchlen <= 33)
{
ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3;
fast_write(f, dst, 3);
dst += 3;
}
else
{
ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3;
fast_write(f, dst, 4);
dst += 4;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#elif QLZ_COMPRESSION_LEVEL == 2
if(matchlen > 2)
{
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if (matchlen < 10)
{
ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5);
fast_write(f, dst, 2);
dst += 2;
}
else
{
ui32 f = best_k | (matchlen << 16) | (hash << 5);
fast_write(f, dst, 3);
dst += 3;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#endif
}
#endif
}
while (src <= last_byte)
{
if ((cword_val & 1) == 1)
{
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
}
#if QLZ_COMPRESSION_LEVEL < 3
if (src <= last_byte - 3)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash, fetch;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
state->hash[hash].offset = CAST(src - OFFSET_BASE);
state->hash[hash].cache = fetch;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(src);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#endif
}
#endif
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
while((cword_val & 1) != 1)
cword_val = (cword_val >> 1);
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
// min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument
return dst - destination < 9 ? 9 : dst - destination;
}
static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history)
{
const unsigned char *src = source + qlz_size_header((const char *)source);
unsigned char *dst = destination;
const unsigned char *last_destination_byte = destination + size - 1;
ui32 cword_val = 1;
const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
unsigned char *last_hashed = destination - 1;
const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1;
static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
(void) last_source_byte;
(void) last_hashed;
(void) state;
(void) history;
for(;;)
{
ui32 fetch;
if (cword_val == 1)
{
#ifdef QLZ_MEMORY_SAFE
if(src + CWORD_LEN - 1 > last_source_byte)
return 0;
#endif
cword_val = fast_read(src, CWORD_LEN);
src += CWORD_LEN;
}
#ifdef QLZ_MEMORY_SAFE
if(src + 4 - 1 > last_source_byte)
return 0;
#endif
fetch = fast_read(src, 4);
if ((cword_val & 1) == 1)
{
ui32 matchlen;
const unsigned char *offset2;
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
cword_val = cword_val >> 1;
hash = (fetch >> 4) & 0xfff;
offset2 = (const unsigned char *)(size_t)state->hash[hash].offset;
if((fetch & 0xf) != 0)
{
matchlen = (fetch & 0xf) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
cword_val = cword_val >> 1;
hash = (fetch >> 5) & 0x7ff;
c = (unsigned char)(fetch & 0x3);
offset2 = state->hash[hash].offset[c];
if((fetch & (28)) != 0)
{
matchlen = ((fetch >> 2) & 0x7) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 3
ui32 offset;
cword_val = cword_val >> 1;
if ((fetch & 3) == 0)
{
offset = (fetch & 0xff) >> 2;
matchlen = 3;
src++;
}
else if ((fetch & 2) == 0)
{
offset = (fetch & 0xffff) >> 2;
matchlen = 3;
src += 2;
}
else if ((fetch & 1) == 0)
{
offset = (fetch & 0xffff) >> 6;
matchlen = ((fetch >> 2) & 15) + 3;
src += 2;
}
else if ((fetch & 127) != 3)
{
offset = (fetch >> 7) & 0x1ffff;
matchlen = ((fetch >> 2) & 0x1f) + 2;
src += 3;
}
else
{
offset = (fetch >> 15);
matchlen = ((fetch >> 7) & 255) + 3;
src += 4;
}
offset2 = dst - offset;
#endif
#ifdef QLZ_MEMORY_SAFE
if(offset2 < history || offset2 > dst - MINOFFSET - 1)
return 0;
if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1))
return 0;
#endif
memcpy_up(dst, offset2, matchlen);
dst += matchlen;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - matchlen);
last_hashed = dst - 1;
#endif
}
else
{
if (dst < last_matchstart)
{
unsigned int n = bitlut[cword_val & 0xf];
#ifdef X86X64
*(ui32 *)dst = *(ui32 *)src;
#else
memcpy_up(dst, src, 4);
#endif
cword_val = cword_val >> n;
dst += n;
src += n;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - 3);
#endif
}
else
{
while(dst <= last_destination_byte)
{
if (cword_val == 1)
{
src += CWORD_LEN;
cword_val = 1U << 31;
}
#ifdef QLZ_MEMORY_SAFE
if(src >= last_source_byte + 1)
return 0;
#endif
*dst = *src;
dst++;
src++;
cword_val = cword_val >> 1;
}
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant
#endif
return size;
}
}
}
}
size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state)
{
size_t r;
ui32 compressed;
size_t base;
if(size == 0 || size > 0xffffffff - 400)
return 0;
if(size < 216)
base = 3;
else
base = 9;
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
reset_table_compress(state);
r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state);
#if QLZ_STREAMING_BUFFER > 0
reset_table_compress(state);
#endif
if(r == base)
{
memcpy(destination + base, source, size);
r = size + base;
compressed = 0;
}
else
{
compressed = 1;
}
state->stream_counter = 0;
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *src = state->stream_buffer + state->stream_counter;
memcpy(src, source, size);
r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state);
if(r == base)
{
memcpy(destination + base, src, size);
r = size + base;
compressed = 0;
reset_table_compress(state);
}
else
{
compressed = 1;
}
state->stream_counter += size;
}
#endif
if(base == 3)
{
*destination = (unsigned char)(0 | compressed);
*(destination + 1) = (unsigned char)r;
*(destination + 2) = (unsigned char)size;
}
else
{
*destination = (unsigned char)(2 | compressed);
fast_write((ui32)r, destination + 1, 4);
fast_write((ui32)size, destination + 5, 4);
}
*destination |= (QLZ_COMPRESSION_LEVEL << 2);
*destination |= (1 << 6);
*destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4);
// 76543210
// 01SSLLHC
return r;
}
size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state)
{
size_t dsiz = qlz_size_decompressed(source);
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
if((*source & 1) == 1)
{
reset_table_decompress(state);
dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination);
}
else
{
memcpy(destination, source + qlz_size_header(source), dsiz);
}
state->stream_counter = 0;
reset_table_decompress(state);
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *dst = state->stream_buffer + state->stream_counter;
if((*source & 1) == 1)
{
dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer);
}
else
{
memcpy(dst, source + qlz_size_header(source), dsiz);
reset_table_decompress(state);
}
memcpy(destination, dst, dsiz);
state->stream_counter += dsiz;
}
#endif
return dsiz;
}

View File

@ -1,843 +0,0 @@
// Fast data compression library
// Copyright (C) 2006-2011 Lasse Mikkel Reinhold
// lar@quicklz.com
//
// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything
// released into public must be open source) or under a commercial license if such
// has been acquired (see http://www.quicklz.com/order.html). The commercial license
// does not cover derived or ported versions created by third parties under GPL.
// 1.5.0 final
#define QLZ_COMPRESSION_LEVEL 3
#include "QuickLZ.h"
#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0
#error quicklz.c and quicklz.h have different versions
#endif
#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64))
#define X86X64
#endif
#define MINOFFSET 2
#define UNCONDITIONAL_MATCHLEN 6
#define UNCOMPRESSED_END 4
#define CWORD_LEN 4
#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
#define OFFSET_BASE source
#define CAST (ui32)(size_t)
#else
#define OFFSET_BASE 0
#define CAST
#endif
int qlz_get_setting(int setting)
{
switch (setting)
{
case 0: return QLZ_COMPRESSION_LEVEL;
case 1: return sizeof(qlz_state_compress);
case 2: return sizeof(qlz_state_decompress);
case 3: return QLZ_STREAMING_BUFFER;
#ifdef QLZ_MEMORY_SAFE
case 6: return 1;
#else
case 6: return 0;
#endif
case 7: return QLZ_VERSION_MAJOR;
case 8: return QLZ_VERSION_MINOR;
case 9: return QLZ_VERSION_REVISION;
}
return -1;
}
#if QLZ_COMPRESSION_LEVEL == 1
static int same(const unsigned char *src, size_t n)
{
while(n > 0 && *(src + n) == *src)
n--;
return n == 0 ? 1 : 0;
}
#endif
static void reset_table_compress(qlz_state_compress *state)
{
int i;
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
#if QLZ_COMPRESSION_LEVEL == 1
state->hash[i].offset = 0;
#else
state->hash_counter[i] = 0;
#endif
}
}
static void reset_table_decompress(qlz_state_decompress *state)
{
int i;
(void)state;
(void)i;
#if QLZ_COMPRESSION_LEVEL == 2
for(i = 0; i < QLZ_HASH_VALUES; i++)
{
state->hash_counter[i] = 0;
}
#endif
}
static __inline ui32 hash_func(ui32 i)
{
#if QLZ_COMPRESSION_LEVEL == 2
return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1);
#else
return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1);
#endif
}
static __inline ui32 fast_read(void const *src, ui32 bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)src;
switch (bytes)
{
case 4:
return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24);
case 3:
return(*p | *(p + 1) << 8 | *(p + 2) << 16);
case 2:
return(*p | *(p + 1) << 8);
case 1:
return(*p);
}
return 0;
#else
if (bytes >= 1 && bytes <= 4)
return *((ui32*)src);
else
return 0;
#endif
}
static __inline ui32 hashat(const unsigned char *src)
{
ui32 fetch, hash;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
return hash;
}
static __inline void fast_write(ui32 f, void *dst, size_t bytes)
{
#ifndef X86X64
unsigned char *p = (unsigned char*)dst;
switch (bytes)
{
case 4:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
*(p + 3) = (unsigned char)(f >> 24);
return;
case 3:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
*(p + 2) = (unsigned char)(f >> 16);
return;
case 2:
*p = (unsigned char)f;
*(p + 1) = (unsigned char)(f >> 8);
return;
case 1:
*p = (unsigned char)f;
return;
}
#else
switch (bytes)
{
case 4:
*((ui32*)dst) = f;
return;
case 3:
*((ui32*)dst) = f;
return;
case 2:
*((ui16 *)dst) = (ui16)f;
return;
case 1:
*((unsigned char*)dst) = (unsigned char)f;
return;
}
#endif
}
size_t qlz_size_decompressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1 + n, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
size_t qlz_size_compressed(const char *source)
{
ui32 n, r;
n = (((*source) & 2) == 2) ? 4 : 1;
r = fast_read(source + 1, n);
r = r & (0xffffffff >> ((4 - n)*8));
return r;
}
static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n)
{
// Caution if modifying memcpy_up! Overlap of dst and src must be special handled.
#ifndef X86X64
unsigned char *end = dst + n;
while(dst < end)
{
*dst = *src;
dst++;
src++;
}
#else
ui32 f = 0;
do
{
*(ui32 *)(dst + f) = *(ui32 *)(src + f);
f += MINOFFSET + 1;
}
while (f < n);
#endif
}
static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
hash = hashat(s);
state->hash[hash].offset = s;
state->hash_counter[hash] = 1;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(s);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s;
c++;
state->hash_counter[hash] = c;
#endif
(void)state;
(void)s;
}
#if QLZ_COMPRESSION_LEVEL <= 2
static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max)
{
while(*lh < max)
{
(*lh)++;
update_hash(state, *lh);
}
}
#endif
static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state)
{
const unsigned char *last_byte = source + size - 1;
const unsigned char *src = source;
unsigned char *cword_ptr = destination;
unsigned char *dst = destination + CWORD_LEN;
ui32 cword_val = 1U << 31;
const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
ui32 fetch = 0;
unsigned int lits = 0;
(void) lits;
if(src <= last_matchstart)
fetch = fast_read(src, 3);
while(src <= last_matchstart)
{
if ((cword_val & 1) == 1)
{
// store uncompressed if compression ratio is too low
if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5))
return 0;
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
fetch = fast_read(src, 3);
}
#if QLZ_COMPRESSION_LEVEL == 1
{
const unsigned char *o;
ui32 hash, cached;
hash = hash_func(fetch);
cached = fetch ^ state->hash[hash].cache;
state->hash[hash].cache = fetch;
o = state->hash[hash].offset + OFFSET_BASE;
state->hash[hash].offset = CAST(src - OFFSET_BASE);
#ifdef X86X64
if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if(cached != 0)
{
#else
if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
{
if (*(o + 3) != *(src + 3))
{
#endif
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
fast_write((3 - 2) | hash, dst, 2);
src += 3;
dst += 2;
}
else
{
const unsigned char *old_src = src;
size_t matchlen;
hash <<= 4;
cword_val = (cword_val >> 1) | (1U << 31);
src += 4;
if(*(o + (src - old_src)) == *src)
{
src++;
if(*(o + (src - old_src)) == *src)
{
size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1;
size_t remaining = q > 255 ? 255 : q;
src++;
while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining)
src++;
}
}
matchlen = src - old_src;
if (matchlen < 18)
{
fast_write((ui32)(matchlen - 2) | hash, dst, 2);
dst += 2;
}
else
{
fast_write((ui32)(matchlen << 16) | hash, dst, 3);
dst += 3;
}
}
fetch = fast_read(src, 3);
lits = 0;
}
else
{
lits++;
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
#ifdef X86X64
fetch = fast_read(src, 3);
#else
fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16);
#endif
}
}
#elif QLZ_COMPRESSION_LEVEL >= 2
{
const unsigned char *o, *offset2;
ui32 hash, matchlen, k, m, best_k = 0;
unsigned char c;
size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1);
(void)best_k;
//hash = hashat(src);
fetch = fast_read(src, 3);
hash = hash_func(fetch);
c = state->hash_counter[hash];
offset2 = state->hash[hash].offset[0];
if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0)
{
matchlen = 3;
if(*(offset2 + matchlen) == *(src + matchlen))
{
matchlen = 4;
while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining)
matchlen++;
}
}
else
matchlen = 0;
for(k = 1; k < QLZ_POINTERS && c > k; k++)
{
o = state->hash[hash].offset[k];
#if QLZ_COMPRESSION_LEVEL == 3
if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#elif QLZ_COMPRESSION_LEVEL == 2
if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
#endif
{
m = 3;
while(*(o + m) == *(src + m) && m < remaining)
m++;
#if QLZ_COMPRESSION_LEVEL == 3
if ((m > matchlen) || (m == matchlen && o > offset2))
#elif QLZ_COMPRESSION_LEVEL == 2
if (m > matchlen)
#endif
{
offset2 = o;
matchlen = m;
best_k = k;
}
}
}
o = offset2;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#if QLZ_COMPRESSION_LEVEL == 3
if(matchlen > 2 && src - o < 131071)
{
ui32 u;
size_t offset = src - o;
for(u = 1; u < matchlen; u++)
{
hash = hashat(src + u);
c = state->hash_counter[hash]++;
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u;
}
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if(matchlen == 3 && offset <= 63)
{
*dst = (unsigned char)(offset << 2);
dst++;
}
else if (matchlen == 3 && offset <= 16383)
{
ui32 f = (ui32)((offset << 2) | 1);
fast_write(f, dst, 2);
dst += 2;
}
else if (matchlen <= 18 && offset <= 1023)
{
ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2;
fast_write(f, dst, 2);
dst += 2;
}
else if(matchlen <= 33)
{
ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3;
fast_write(f, dst, 3);
dst += 3;
}
else
{
ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3;
fast_write(f, dst, 4);
dst += 4;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#elif QLZ_COMPRESSION_LEVEL == 2
if(matchlen > 2)
{
cword_val = (cword_val >> 1) | (1U << 31);
src += matchlen;
if (matchlen < 10)
{
ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5);
fast_write(f, dst, 2);
dst += 2;
}
else
{
ui32 f = best_k | (matchlen << 16) | (hash << 5);
fast_write(f, dst, 3);
dst += 3;
}
}
else
{
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
#endif
}
#endif
}
while (src <= last_byte)
{
if ((cword_val & 1) == 1)
{
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
cword_ptr = dst;
dst += CWORD_LEN;
cword_val = 1U << 31;
}
#if QLZ_COMPRESSION_LEVEL < 3
if (src <= last_byte - 3)
{
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash, fetch;
fetch = fast_read(src, 3);
hash = hash_func(fetch);
state->hash[hash].offset = CAST(src - OFFSET_BASE);
state->hash[hash].cache = fetch;
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
hash = hashat(src);
c = state->hash_counter[hash];
state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
c++;
state->hash_counter[hash] = c;
#endif
}
#endif
*dst = *src;
src++;
dst++;
cword_val = (cword_val >> 1);
}
while((cword_val & 1) != 1)
cword_val = (cword_val >> 1);
fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
// min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument
return dst - destination < 9 ? 9 : dst - destination;
}
extern size_t qlz_size_header(const char *source);
static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history)
{
const unsigned char *src = source + qlz_size_header((const char *)source);
unsigned char *dst = destination;
const unsigned char *last_destination_byte = destination + size - 1;
ui32 cword_val = 1;
const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
unsigned char *last_hashed = destination - 1;
const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1;
static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
(void) last_source_byte;
(void) last_hashed;
(void) state;
(void) history;
for(;;)
{
ui32 fetch;
if (cword_val == 1)
{
#ifdef QLZ_MEMORY_SAFE
if(src + CWORD_LEN - 1 > last_source_byte)
return 0;
#endif
cword_val = fast_read(src, CWORD_LEN);
src += CWORD_LEN;
}
#ifdef QLZ_MEMORY_SAFE
if(src + 4 - 1 > last_source_byte)
return 0;
#endif
fetch = fast_read(src, 4);
if ((cword_val & 1) == 1)
{
ui32 matchlen;
const unsigned char *offset2;
#if QLZ_COMPRESSION_LEVEL == 1
ui32 hash;
cword_val = cword_val >> 1;
hash = (fetch >> 4) & 0xfff;
offset2 = (const unsigned char *)(size_t)state->hash[hash].offset;
if((fetch & 0xf) != 0)
{
matchlen = (fetch & 0xf) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 2
ui32 hash;
unsigned char c;
cword_val = cword_val >> 1;
hash = (fetch >> 5) & 0x7ff;
c = (unsigned char)(fetch & 0x3);
offset2 = state->hash[hash].offset[c];
if((fetch & (28)) != 0)
{
matchlen = ((fetch >> 2) & 0x7) + 2;
src += 2;
}
else
{
matchlen = *(src + 2);
src += 3;
}
#elif QLZ_COMPRESSION_LEVEL == 3
ui32 offset;
cword_val = cword_val >> 1;
if ((fetch & 3) == 0)
{
offset = (fetch & 0xff) >> 2;
matchlen = 3;
src++;
}
else if ((fetch & 2) == 0)
{
offset = (fetch & 0xffff) >> 2;
matchlen = 3;
src += 2;
}
else if ((fetch & 1) == 0)
{
offset = (fetch & 0xffff) >> 6;
matchlen = ((fetch >> 2) & 15) + 3;
src += 2;
}
else if ((fetch & 127) != 3)
{
offset = (fetch >> 7) & 0x1ffff;
matchlen = ((fetch >> 2) & 0x1f) + 2;
src += 3;
}
else
{
offset = (fetch >> 15);
matchlen = ((fetch >> 7) & 255) + 3;
src += 4;
}
offset2 = dst - offset;
#endif
#ifdef QLZ_MEMORY_SAFE
if(offset2 < history || offset2 > dst - MINOFFSET - 1)
return 0;
if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1))
return 0;
#endif
memcpy_up(dst, offset2, matchlen);
dst += matchlen;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - matchlen);
last_hashed = dst - 1;
#endif
}
else
{
if (dst < last_matchstart)
{
unsigned int n = bitlut[cword_val & 0xf];
#ifdef X86X64
*(ui32 *)dst = *(ui32 *)src;
#else
memcpy_up(dst, src, 4);
#endif
cword_val = cword_val >> n;
dst += n;
src += n;
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, dst - 3);
#endif
}
else
{
while(dst <= last_destination_byte)
{
if (cword_val == 1)
{
src += CWORD_LEN;
cword_val = 1U << 31;
}
#ifdef QLZ_MEMORY_SAFE
if(src >= last_source_byte + 1)
return 0;
#endif
*dst = *src;
dst++;
src++;
cword_val = cword_val >> 1;
}
#if QLZ_COMPRESSION_LEVEL <= 2
update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant
#endif
return size;
}
}
}
}
size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state)
{
size_t r;
ui32 compressed;
size_t base;
if(size == 0 || size > 0xffffffff - 400)
return 0;
if(size < 216)
base = 3;
else
base = 9;
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
reset_table_compress(state);
r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state);
#if QLZ_STREAMING_BUFFER > 0
reset_table_compress(state);
#endif
if(r == base)
{
memcpy(destination + base, source, size);
r = size + base;
compressed = 0;
}
else
{
compressed = 1;
}
state->stream_counter = 0;
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *src = state->stream_buffer + state->stream_counter;
memcpy(src, source, size);
r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state);
if(r == base)
{
memcpy(destination + base, src, size);
r = size + base;
compressed = 0;
reset_table_compress(state);
}
else
{
compressed = 1;
}
state->stream_counter += size;
}
#endif
if(base == 3)
{
*destination = (unsigned char)(0 | compressed);
*(destination + 1) = (unsigned char)r;
*(destination + 2) = (unsigned char)size;
}
else
{
*destination = (unsigned char)(2 | compressed);
fast_write((ui32)r, destination + 1, 4);
fast_write((ui32)size, destination + 5, 4);
}
*destination |= (QLZ_COMPRESSION_LEVEL << 2);
*destination |= (1 << 6);
*destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4);
// 76543210
// 01SSLLHC
return r;
}
size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state)
{
size_t dsiz = qlz_size_decompressed(source);
#if QLZ_STREAMING_BUFFER > 0
if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER)
#endif
{
if((*source & 1) == 1)
{
reset_table_decompress(state);
dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination);
}
else
{
memcpy(destination, source + qlz_size_header(source), dsiz);
}
state->stream_counter = 0;
reset_table_decompress(state);
}
#if QLZ_STREAMING_BUFFER > 0
else
{
unsigned char *dst = state->stream_buffer + state->stream_counter;
if((*source & 1) == 1)
{
dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer);
}
else
{
memcpy(dst, source + qlz_size_header(source), dsiz);
reset_table_decompress(state);
}
memcpy(destination, dst, dsiz);
state->stream_counter += dsiz;
}
#endif
return dsiz;
}

View File

@ -34,7 +34,7 @@ test_vector_t swap_elements(test_vector_t vector, int per, int max_distance) {
} }
bool test_vector(const std::string_view& name, const test_vector_t& vector) { bool test_vector(const std::string_view& name, const test_vector_t& vector) {
generation_estimator gen{}; GenerationEstimator gen{};
size_t last_value{0}, last_gen{0}, index{0}; size_t last_value{0}, last_gen{0}, index{0};
for(auto [id, exp] : vector) { for(auto [id, exp] : vector) {
@ -50,7 +50,7 @@ bool test_vector(const std::string_view& name, const test_vector_t& vector) {
} }
template <size_t N> template <size_t N>
bool test_vector(generation_estimator& generator, const std::array<uint16_t, N>& packet_ids, const std::array<uint16_t, N>& expected) { bool test_vector(GenerationEstimator& generator, const std::array<uint16_t, N>& packet_ids, const std::array<uint16_t, N>& expected) {
for(size_t index = 0; index < N; index++) { for(size_t index = 0; index < N; index++) {
auto result = generator.visit_packet(packet_ids[index]); auto result = generator.visit_packet(packet_ids[index]);
if(result != expected[index]) { if(result != expected[index]) {
@ -67,7 +67,7 @@ bool test_vector(generation_estimator& generator, const std::array<uint16_t, N>&
} }
int main() { int main() {
generation_estimator gen{}; GenerationEstimator gen{};
{ {
test_vector("00 loss", generate_test_vector(0x30000, 0)); test_vector("00 loss", generate_test_vector(0x30000, 0));