more work towards dictionary text compression

This commit is contained in:
FeralChild64 committed 2020-06-23 06:45:06 +02:00
1 parent 16e84dd562
commit 5ee98ac312
2 files changed
+152 -25

No files matched your search

+1 -1
View File
@@ -126,7 +126,7 @@
#define CONFIG_TAPE_WEDGE #define CONFIG_TAPE_WEDGE
// #define CONFIG_TAPE_HEAD_ALIGN // #define CONFIG_TAPE_HEAD_ALIGN
#define CONFIG_BCD_SAFE_INTERRUPTS #define CONFIG_BCD_SAFE_INTERRUPTS
// #define CONFIG_COMPRESSION_LVL_2 // work-in-progress
// Eye candy // Eye candy
+151 -24
View File
@@ -4,10 +4,6 @@
// XXX print somewhere configuration file used to generate strings // XXX print somewhere configuration file used to generate strings
// XXX use DualStream class for logging // XXX use DualStream class for logging
// XXX use dictionary compression by finding the set of non-overlapping strings that can be concatenated
// to produce full set of strings; some idea for the algorithm (not sure if proper one) is available here
// https://stackoverflow.com/questions/9195676/finding-the-smallest-number-of-substrings-to-represent-a-set-of-strings
// needs some investigation...
#include "common.h" #include "common.h"
@@ -56,8 +52,8 @@ typedef struct StringEntryList
std::vector<StringEntry> list; std::vector<StringEntry> list;
} StringEntryList; } StringEntryList;
typedef std::vector<uint8_t> StringFreqEncoded; typedef std::vector<uint8_t> StringFreqEncoded;
typedef std::vector<uint32_t> StringDictEncoded; typedef std::vector<uint8_t> StringDictEncoded;
typedef struct StringEncodedList typedef struct StringEncodedList
{ {
@@ -462,6 +458,22 @@ std::map<std::string, bool> GLOBAL_ConfigOptions;
// Work class definitions // Work class definitions
// //
class DictEncoder
{
public:
void addString(const std::string &inString, StringDictEncoded *outPtr);
void process(StringEntryList &outDictionary);
private:
bool optimizeSplit();
std::vector<StringDictEncoded *> encodings;
std::vector<std::string> dictionary;
};
class DataSet class DataSet
{ {
public: public:
@@ -475,8 +487,10 @@ private:
void process(); void process();
void generateConfigDepStrings(); void generateConfigDepStrings();
void validateLists();
void calculateFrequencies(); void calculateFrequencies();
void encodeStrings(); void encodeStringsDict();
void encodeStringsFreq();
void encodeByFreq(const std::string &plain, StringFreqEncoded &encoded) const; void encodeByFreq(const std::string &plain, StringFreqEncoded &encoded) const;
@@ -484,6 +498,8 @@ private:
void putCharEncoding(std::ostringstream &stream, uint8_t idx, char character, bool is3n); void putCharEncoding(std::ostringstream &stream, uint8_t idx, char character, bool is3n);
bool isCompressionLvl2(const StringEntryList &list) const;
virtual bool isRelevant(const StringEntry &entry) const = 0; virtual bool isRelevant(const StringEntry &entry) const = 0;
virtual std::string layoutName() const = 0; virtual std::string layoutName() const = 0;
@@ -496,7 +512,7 @@ private:
uint8_t tk__packed_as_3n = 0; uint8_t tk__packed_as_3n = 0;
uint8_t tk__max_keyword_len = 0; uint8_t tk__max_keyword_len = 0;
size_t maxAliasLen = 0; size_t maxAliasLen = 0;
std::string outFileContent; std::string outFileContent;
}; };
@@ -522,11 +538,70 @@ class DataSetX16 : public DataSet
// Work class implementation // Work class implementation
// //
void DictEncoder::addString(const std::string &inString, StringDictEncoded *outPtr)
{
// Store the pointer to encoding
encodings.push_back(outPtr);
// Check if string is already present in the dictionary
auto pos = std::find(dictionary.begin(), dictionary.end(), inString);
// Create initial encoding
if (pos != dictionary.end())
{
encodings.back()->push_back(pos - dictionary.begin());
}
else
{
if (dictionary.size() > 255)
{
ERROR("max 255 strings for dictionary compression");
}
dictionary.push_back(inString);
encodings.back()->push_back(dictionary.size() - 1);
}
}
bool DictEncoder::optimizeSplit()
{
// XXX
return false;
}
void DictEncoder::process(StringEntryList &outDictionary)
{
// Optimize as long as it brings any improvement
while (optimizeSplit()) ;
// Export the dictionary to external format
outDictionary.type = ListType::DICTIONARY;
outDictionary.name = "DICTIONARY";
for (const auto &dictionaryStr : dictionary)
{
StringEntry newEntry = { true, true, true, "", dictionaryStr };
outDictionary.list.push_back(newEntry);
}
}
bool DataSet::isCompressionLvl2(const StringEntryList &list) const
{
return (GLOBAL_ConfigOptions["CONFIG_COMPRESSION_LVL_2"] && list.type == ListType::STRINGS_BASIC);
}
void DataSet::addStrings(const StringEntryList &stringList) void DataSet::addStrings(const StringEntryList &stringList)
{ {
// Import the new list of strings // Import the new list of strings
stringEntryLists.push_back(stringList); stringEntryLists.push_back(stringList);
stringEncodedLists.emplace_back();
// Clear strings not relevant for the current configuration // Clear strings not relevant for the current configuration
@@ -551,8 +626,10 @@ void DataSet::process()
std::cout << "Processing layout '" << layoutName() << "'" << std::endl; std::cout << "Processing layout '" << layoutName() << "'" << std::endl;
generateConfigDepStrings(); generateConfigDepStrings();
validateLists();
encodeStringsDict();
calculateFrequencies(); calculateFrequencies();
encodeStrings(); encodeStringsFreq();
prepareOutput(); prepareOutput();
} }
@@ -661,22 +738,12 @@ void DataSet::generateConfigDepStrings()
} }
} }
void DataSet::calculateFrequencies() void DataSet::validateLists()
{ {
as1n.clear();
as3n.clear();
std::map<char, uint16_t> freqMapGeneral; // general character frequency map
std::map<char, uint16_t> freqMapKeywords; // frequency map for keywords
// Calculate frequencies of characters in the strings
for (const auto &stringEntryList : stringEntryLists) for (const auto &stringEntryList : stringEntryLists)
{ {
for (const auto &stringEntry : stringEntryList.list) for (const auto &stringEntry : stringEntryList.list)
{ {
if (!isRelevant(stringEntry)) continue;
// Check for maximum allowed string length // Check for maximum allowed string length
if (stringEntry.string.length() > 255) ERROR("string cannot be longer than 255 characters"); if (stringEntry.string.length() > 255) ERROR("string cannot be longer than 255 characters");
@@ -696,7 +763,62 @@ void DataSet::calculateFrequencies()
{ {
ERROR(std::string("character above 0x80 in string '") + stringEntry.string + "'"); ERROR(std::string("character above 0x80 in string '") + stringEntry.string + "'");
} }
}
}
}
}
void DataSet::encodeStringsDict()
{
DictEncoder dictEncoder;
// Add strings for dictionary compresssion
for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++)
{
const auto &stringEntryList = stringEntryLists[idx];
auto &stringEncodedList = stringEncodedLists[idx];
// Skip lists not to be encoded using the dictionary
if (!isCompressionLvl2(stringEntryList)) continue;
stringEncodedList.byDict.resize(stringEntryList.list.size());
for (uint8_t idxEntry = 0; idxEntry < stringEntryList.list.size(); idxEntry++)
{
dictEncoder.addString(stringEntryList.list[idxEntry].string,
&stringEncodedList.byDict[idxEntry]);
}
}
// Perform the compression
stringEntryLists.emplace_back();
stringEncodedLists.emplace_back();
dictEncoder.process(stringEntryLists.back());
}
void DataSet::calculateFrequencies()
{
as1n.clear();
as3n.clear();
std::map<char, uint16_t> freqMapGeneral; // general character frequency map
std::map<char, uint16_t> freqMapKeywords; // frequency map for keywords
// Calculate frequencies of characters in the strings
for (const auto &stringEntryList : stringEntryLists)
{
// Skip lists encoded by the dictionary
if (isCompressionLvl2(stringEntryList)) continue;
for (const auto &stringEntry : stringEntryList.list)
{
if (!isRelevant(stringEntry)) continue;
for (const auto &character : stringEntry.string)
{
freqMapGeneral[character]++; freqMapGeneral[character]++;
if (stringEntryList.type == ListType::KEYWORDS) freqMapKeywords[character]++; if (stringEntryList.type == ListType::KEYWORDS) freqMapKeywords[character]++;
} }
@@ -822,16 +944,21 @@ void DataSet::encodeByFreq(const std::string &plain, StringFreqEncoded &encoded)
if (encoded.size() == 0 || encoded.back() != 0) encoded.push_back(0); if (encoded.size() == 0 || encoded.back() != 0) encoded.push_back(0);
} }
void DataSet::encodeStrings() void DataSet::encodeStringsFreq()
{ {
stringEncodedLists.clear(); stringEncodedLists.clear();
// Encode every relevant string from every list - by character frequency // Encode every relevant string from every list - by character frequency
for (const auto &stringEntryList : stringEntryLists) for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++)
{ {
stringEncodedLists.emplace_back(); const auto &stringEntryList = stringEntryLists[idx];
auto &stringEncodedList = stringEncodedLists.back(); auto &stringEncodedList = stringEncodedLists[idx];
// Skip lists encoded by the dictionary
if (isCompressionLvl2(stringEntryList)) continue;
// Perform encoding of the list
for (const auto &stringEntry : stringEntryList.list) for (const auto &stringEntry : stringEntryList.list)
{ {