parent
8aa9afb30e
commit
50479cb07c
4 changed files with 869 additions and 3 deletions
@ -0,0 +1,749 @@ |
|||||||
|
#define STB_DEFINE |
||||||
|
#include "../stb.h" |
||||||
|
|
||||||
|
// create unicode mappings
|
||||||
|
//
|
||||||
|
// Two kinds of mappings:
|
||||||
|
// map to a number
|
||||||
|
// map to a bit
|
||||||
|
//
|
||||||
|
// For mapping to a number, we use the following strategy:
|
||||||
|
//
|
||||||
|
// User supplies:
|
||||||
|
// 1. a table of numbers (for now we use uint16, so full Unicode table is 4MB)
|
||||||
|
// 2. a "don't care" value
|
||||||
|
// 3. define a 'fallback' value (typically 0)
|
||||||
|
// 4. define a fast-path range (typically 0..255 or 0..1023) [@TODO: automate detecting this]
|
||||||
|
//
|
||||||
|
// Code:
|
||||||
|
// 1. Determine range of *end* of unicode codepoints (U+10FFFF and down) which
|
||||||
|
// all have the same value (or don't care). If large enough, emit this as a
|
||||||
|
// special case in the code.
|
||||||
|
// 2. Repeat above, limited to at most U+FFFF.
|
||||||
|
// 3. Cluster the data into intervals of 8,16,32,64,128,256 numeric values.
|
||||||
|
// 3a. If all the values in an interval are fallback/dont-care, no further processing
|
||||||
|
// 3b. Find the "trimmed range" outside which all the values are the fallback or don't care
|
||||||
|
// 3c. Find the "special trimmed range" outside which all the values are some constant or don't care
|
||||||
|
// 4. Pack the clusters into continuous memory, and find previous instances of
|
||||||
|
// the cluster. Repeat for trimmed & special-trimmed. In the first case, find
|
||||||
|
// previous instances of the cluster (allow don't-care to match in either
|
||||||
|
// direction), both aligned and mis-aligned; in the latter, starting where
|
||||||
|
// things start or mis-aligned. Build an index table specifiying the
|
||||||
|
// location of each cluster (and its length). Allow an extra indirection here;
|
||||||
|
// the full-sized index can index a smaller table which has the actual offset
|
||||||
|
// (and lengths).
|
||||||
|
// 5. Associate with each packed continuous memory above the amount of memory
|
||||||
|
// required to store the data w/ smallest datatype (of uint8, uint16, uint32).
|
||||||
|
// Discard the continuous memory. Recurse on each index table, but avoid the
|
||||||
|
// smaller packing.
|
||||||
|
//
|
||||||
|
// For mapping to a bit, we pack the results for 8 characters into a byte, and then apply
|
||||||
|
// the above strategy. Note that there may be more optimal approaches with e.g. packing
|
||||||
|
// 8 different bits into a single structure, though, which we should explore eventually.
|
||||||
|
|
||||||
|
|
||||||
|
// currently we limit *indices* to being 2^16, and we pack them as
|
||||||
|
// index + end_trim*2^16 + start_trim*2^24; specials have to go in a separate table
|
||||||
|
typedef uint32 uval; |
||||||
|
#define UVAL_DONT_CARE_DEFAULT 0xffffffff |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
uval *input; |
||||||
|
uint32 dont_care; |
||||||
|
uint32 fallback; |
||||||
|
int fastpath; |
||||||
|
int length; |
||||||
|
int depth; |
||||||
|
int has_sign; |
||||||
|
int splittable; |
||||||
|
int replace_fallback_with_codepoint; |
||||||
|
size_t input_size; |
||||||
|
size_t inherited_storage; |
||||||
|
} table; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
int split_log2; |
||||||
|
table result; // index into not-returned table
|
||||||
|
int storage; |
||||||
|
} output; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
table t; |
||||||
|
char **output_name; |
||||||
|
} info; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
size_t path; |
||||||
|
size_t size; |
||||||
|
} result; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
uint8 trim_end; |
||||||
|
uint8 trim_start; |
||||||
|
uint8 special; |
||||||
|
uint8 aligned; |
||||||
|
uint8 indirect; |
||||||
|
|
||||||
|
uint16 overhead; // add some forced overhead for each mode to avoid getting complex encoding when it doesn't save much
|
||||||
|
|
||||||
|
} mode_info; |
||||||
|
|
||||||
|
mode_info modes[] = |
||||||
|
{ |
||||||
|
{ 0,0,0,0,0, 32, }, |
||||||
|
{ 0,0,0,0,1, 100, }, |
||||||
|
{ 0,0,0,1,0, 32, }, |
||||||
|
{ 0,0,0,1,1, 100, }, |
||||||
|
{ 0,0,1,0,1, 100, }, |
||||||
|
{ 0,0,1,1,0, 32, }, |
||||||
|
{ 0,0,1,1,1, 200, }, |
||||||
|
{ 1,0,0,0,0, 100, }, |
||||||
|
{ 1,0,0,0,1, 120, }, |
||||||
|
{ 1,1,0,0,0, 100, }, |
||||||
|
{ 1,1,0,0,1, 130, }, |
||||||
|
{ 1,0,1,0,0, 130, }, |
||||||
|
{ 1,0,1,0,1, 180, }, |
||||||
|
{ 1,1,1,0,0, 180, }, |
||||||
|
{ 1,1,1,0,1, 200, }, |
||||||
|
}; |
||||||
|
|
||||||
|
#define MODECOUNT (sizeof(modes)/sizeof(modes[0])) |
||||||
|
#define CLUSTERSIZECOUNT 6 // 8,16, 32,64, 128,256
|
||||||
|
|
||||||
|
size_t size_for_max_number(uint32 number) |
||||||
|
{ |
||||||
|
if (number == 0) return 0; |
||||||
|
if (number < 256) return 1; |
||||||
|
if (number < 256*256) return 2; |
||||||
|
if (number < 256*256*256) return 3; |
||||||
|
return 4; |
||||||
|
} |
||||||
|
|
||||||
|
size_t size_for_max_number_aligned(uint32 number) |
||||||
|
{ |
||||||
|
size_t n = size_for_max_number(number); |
||||||
|
return n == 3 ? 4 : n; |
||||||
|
} |
||||||
|
|
||||||
|
uval get_data(uval *data, int offset, uval *end) |
||||||
|
{ |
||||||
|
if (data + offset >= end) |
||||||
|
return 0; |
||||||
|
else |
||||||
|
return data[offset]; |
||||||
|
} |
||||||
|
|
||||||
|
int safe_len(uval *data, int len, uval *end) |
||||||
|
{ |
||||||
|
if (len > end - data) |
||||||
|
return end - data; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
uval tempdata[256]; |
||||||
|
int dirty=0; |
||||||
|
|
||||||
|
size_t find_packed(uval **packed, uval *data, int len, int aligned, int fastpath, uval *end, int offset, int replace) |
||||||
|
{ |
||||||
|
int packlen = stb_arr_len(*packed); |
||||||
|
int i,p; |
||||||
|
|
||||||
|
if (data+len > end || replace) { |
||||||
|
int safelen = safe_len(data, len, end); |
||||||
|
memset(tempdata, 0, dirty*sizeof(tempdata[0])); |
||||||
|
memcpy(tempdata, data, safelen * sizeof(data[0])); |
||||||
|
data = tempdata; |
||||||
|
dirty = len; |
||||||
|
} |
||||||
|
if (replace) { |
||||||
|
int i; |
||||||
|
int safelen = safe_len(data, len, end); |
||||||
|
for (i=0; i < safelen; ++i) |
||||||
|
if (data[i] == 0) |
||||||
|
data[i] = offset+i; |
||||||
|
} |
||||||
|
|
||||||
|
if (len <= 0) |
||||||
|
return 0; |
||||||
|
if (!fastpath) { |
||||||
|
if (aligned) { |
||||||
|
for (i=0; i < packlen; i += len) |
||||||
|
if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) |
||||||
|
return i / len; |
||||||
|
} else { |
||||||
|
for (i=0; i < packlen-len+1; i += 1 ) |
||||||
|
if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
p = stb_arr_len(*packed); |
||||||
|
for (i=0; i < len; ++i) |
||||||
|
stb_arr_push(*packed, data[i]); |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
void output_table(char *name1, char *name2, uval *data, int length, int sign, char **names) |
||||||
|
{ |
||||||
|
char temp[20]; |
||||||
|
uval maxv = 0; |
||||||
|
int bytes, numlen, at_newline; |
||||||
|
int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple?
|
||||||
|
int i,pos, do_split=0; |
||||||
|
for (i=0; i < length; ++i) |
||||||
|
if (sign) |
||||||
|
maxv = stb_max(maxv, (uval)abs((int)data[i])); |
||||||
|
else |
||||||
|
maxv = stb_max(maxv, data[i]); |
||||||
|
bytes = size_for_max_number_aligned(maxv); |
||||||
|
sprintf(temp, "%d", maxv); |
||||||
|
numlen=strlen(temp); |
||||||
|
if (sign) |
||||||
|
++numlen; |
||||||
|
|
||||||
|
if (bytes == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
printf("uint%d %s%s[%d] = {\n", bytes*8, name1, name2, length); |
||||||
|
at_newline = 1; |
||||||
|
for (i=0; i < length; ++i) { |
||||||
|
if (pos + numlen + 2 > linelen) { |
||||||
|
printf("\n"); |
||||||
|
at_newline = 1; |
||||||
|
pos = 0; |
||||||
|
} |
||||||
|
if (at_newline) { |
||||||
|
printf(" "); |
||||||
|
pos = 2; |
||||||
|
at_newline = 0; |
||||||
|
} else { |
||||||
|
printf(" "); |
||||||
|
++pos; |
||||||
|
} |
||||||
|
printf("%*d,", numlen, data[i]); |
||||||
|
pos += numlen+1; |
||||||
|
} |
||||||
|
if (!at_newline) printf("\n"); |
||||||
|
printf("};\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void output_table_with_trims(char *name1, char *name2, uval *data, int length) |
||||||
|
{ |
||||||
|
uval maxt=0, maxp=0; |
||||||
|
int i,d,s,e, count; |
||||||
|
// split the table into two pieces
|
||||||
|
uval *trims = NULL; |
||||||
|
|
||||||
|
if (length == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
for (i=0; i < stb_arr_len(data); ++i) { |
||||||
|
stb_arr_push(trims, data[i] >> 16); |
||||||
|
data[i] &= 0xffff; |
||||||
|
maxt = stb_max(maxt, trims[i]); |
||||||
|
maxp = stb_max(maxp, data[i]); |
||||||
|
} |
||||||
|
|
||||||
|
d=s=e=1; |
||||||
|
if (maxt >= 256) { |
||||||
|
// need to output start & end values
|
||||||
|
if (maxp >= 256) { |
||||||
|
// can pack into a single table
|
||||||
|
printf("struct { uint16 val; uint8 start, end; } %s%s[%d] = {\n", name1, name2, length); |
||||||
|
} else { |
||||||
|
output_table(name1, name2, data, length, 0, 0); |
||||||
|
d=0; |
||||||
|
printf("struct { uint8 start, end; } %s%s_trim[%d] = {\n", name1, name2, length); |
||||||
|
} |
||||||
|
} else if (maxt > 0) { |
||||||
|
if (maxp >= 256) { |
||||||
|
output_table(name1, name2, data, length, 0, 0); |
||||||
|
output_table(name1, stb_sprintf("%s_end", name2), trims, length, 0, 0); |
||||||
|
return; |
||||||
|
} else { |
||||||
|
printf("struct { uint8 val, end; } %s%s[%d] = {\n", name1, name2, length); |
||||||
|
s=0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
output_table(name1, name2, data, length, 0, 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
// d or s can be zero (but not both), e is always present and last
|
||||||
|
count = d + s + e; |
||||||
|
assert(count >= 2 && count <= 3); |
||||||
|
|
||||||
|
{ |
||||||
|
char temp[60]; |
||||||
|
uval maxv = 0; |
||||||
|
int numlen, at_newline, len; |
||||||
|
int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple?
|
||||||
|
int i,pos, do_split=0; |
||||||
|
numlen = 0; |
||||||
|
for (i=0; i < length; ++i) { |
||||||
|
if (count == 2) |
||||||
|
sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); |
||||||
|
else |
||||||
|
sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); |
||||||
|
len = strlen(temp); |
||||||
|
numlen = stb_max(len, numlen); |
||||||
|
} |
||||||
|
|
||||||
|
at_newline = 1; |
||||||
|
for (i=0; i < length; ++i) { |
||||||
|
if (pos + numlen + 2 > linelen) { |
||||||
|
printf("\n"); |
||||||
|
at_newline = 1; |
||||||
|
pos = 0; |
||||||
|
} |
||||||
|
if (at_newline) { |
||||||
|
printf(" "); |
||||||
|
pos = 2; |
||||||
|
at_newline = 0; |
||||||
|
} else { |
||||||
|
printf(" "); |
||||||
|
++pos; |
||||||
|
} |
||||||
|
if (count == 2) |
||||||
|
sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); |
||||||
|
else |
||||||
|
sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); |
||||||
|
printf("%*s,", numlen, temp); |
||||||
|
pos += numlen+1; |
||||||
|
} |
||||||
|
if (!at_newline) printf("\n"); |
||||||
|
printf("};\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int weight=1; |
||||||
|
|
||||||
|
table pack_for_mode(table *t, int mode, char *table_name) |
||||||
|
{ |
||||||
|
size_t extra_size; |
||||||
|
int i; |
||||||
|
uval maxv; |
||||||
|
mode_info mi = modes[mode % MODECOUNT]; |
||||||
|
int size = 8 << (mode / MODECOUNT); |
||||||
|
table newtab; |
||||||
|
uval *packed = NULL; |
||||||
|
uval *index = NULL; |
||||||
|
uval *indirect = NULL; |
||||||
|
uval *specials = NULL; |
||||||
|
newtab.dont_care = UVAL_DONT_CARE_DEFAULT; |
||||||
|
if (table_name) |
||||||
|
printf("// clusters of %d\n", size); |
||||||
|
for (i=0; i < t->length; i += size) { |
||||||
|
uval newval; |
||||||
|
int fastpath = (i < t->fastpath); |
||||||
|
if (mi.special) { |
||||||
|
int end_trim = size-1; |
||||||
|
int start_trim = 0; |
||||||
|
uval special; |
||||||
|
// @TODO: pick special from start or end instead of only end depending on which is longer
|
||||||
|
for(;;) { |
||||||
|
special = t->input[i + end_trim]; |
||||||
|
if (special != t->dont_care || end_trim == 0) |
||||||
|
break; |
||||||
|
--end_trim; |
||||||
|
} |
||||||
|
// at this point, special==inp[end_trim], and end_trim >= 0
|
||||||
|
if (special == t->dont_care && !fastpath) { |
||||||
|
// entire block is don't care, so OUTPUT don't care
|
||||||
|
stb_arr_push(index, newtab.dont_care); |
||||||
|
continue; |
||||||
|
} else { |
||||||
|
uval pos, trim; |
||||||
|
if (mi.trim_end && !fastpath) { |
||||||
|
while (end_trim >= 0) { |
||||||
|
if (t->input[i + end_trim] == special || t->input[i + end_trim] == t->dont_care) |
||||||
|
--end_trim; |
||||||
|
else |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (mi.trim_start && !fastpath) { |
||||||
|
while (start_trim < end_trim) { |
||||||
|
if (t->input[i + start_trim] == special || t->input[i + start_trim] == t->dont_care) |
||||||
|
++start_trim; |
||||||
|
else |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// end_trim points to the last character we have to output
|
||||||
|
|
||||||
|
// find the first match, or add it
|
||||||
|
pos = find_packed(&packed, &t->input[i+start_trim], end_trim-start_trim+1, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); |
||||||
|
|
||||||
|
// encode as a uval
|
||||||
|
if (!mi.trim_end) { |
||||||
|
if (end_trim == 0) |
||||||
|
pos = special; |
||||||
|
else |
||||||
|
pos = pos | 0x80000000; |
||||||
|
} else { |
||||||
|
assert(end_trim < size && end_trim >= -1); |
||||||
|
if (!fastpath) assert(end_trim < size-1); // special always matches last one
|
||||||
|
assert(end_trim < size && end_trim+1 >= 0); |
||||||
|
if (!fastpath) assert(end_trim+1 < size); |
||||||
|
|
||||||
|
if (mi.trim_start) |
||||||
|
trim = start_trim*256 + (end_trim+1); |
||||||
|
else |
||||||
|
trim = end_trim+1; |
||||||
|
|
||||||
|
assert(pos < 65536); // @TODO: if this triggers, just bail on this search path
|
||||||
|
pos = pos + (trim << 16); |
||||||
|
} |
||||||
|
|
||||||
|
newval = pos; |
||||||
|
|
||||||
|
stb_arr_push(specials, special); |
||||||
|
} |
||||||
|
} else if (mi.trim_end) { |
||||||
|
int end_trim = size-1; |
||||||
|
int start_trim = 0; |
||||||
|
uval pos, trim; |
||||||
|
|
||||||
|
while (end_trim >= 0 && !fastpath) |
||||||
|
if (t->input[i + end_trim] == t->fallback || t->input[i + end_trim] == t->dont_care) |
||||||
|
--end_trim; |
||||||
|
else |
||||||
|
break; |
||||||
|
|
||||||
|
if (mi.trim_start && !fastpath) { |
||||||
|
while (start_trim < end_trim) { |
||||||
|
if (t->input[i + start_trim] == t->fallback || t->input[i + start_trim] == t->dont_care) |
||||||
|
++start_trim; |
||||||
|
else |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// end_trim points to the last character we have to output, and can be -1
|
||||||
|
++end_trim; // make exclusive at end
|
||||||
|
|
||||||
|
if (end_trim == 0 && size == 256) |
||||||
|
start_trim = end_trim = 1; // we can't make encode a length from 0..256 in 8 bits, so restrict end_trim to 1..256
|
||||||
|
|
||||||
|
// find the first match, or add it
|
||||||
|
pos = find_packed(&packed, &t->input[i+start_trim], end_trim - start_trim, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); |
||||||
|
|
||||||
|
assert(end_trim <= size && end_trim >= 0); |
||||||
|
if (size == 256) |
||||||
|
assert(end_trim-1 < 256 && end_trim-1 >= 0); |
||||||
|
else |
||||||
|
assert(end_trim < 256 && end_trim >= 0); |
||||||
|
if (size == 256) |
||||||
|
--end_trim; |
||||||
|
|
||||||
|
if (mi.trim_start) |
||||||
|
trim = start_trim*256 + end_trim; |
||||||
|
else |
||||||
|
trim = end_trim; |
||||||
|
|
||||||
|
assert(pos < 65536); // @TODO: if this triggers, just bail on this search path
|
||||||
|
pos = pos + (trim << 16); |
||||||
|
|
||||||
|
newval = pos; |
||||||
|
} else { |
||||||
|
newval = find_packed(&packed, &t->input[i], size, mi.aligned, fastpath, &t->input[t->length], i, t->replace_fallback_with_codepoint); |
||||||
|
} |
||||||
|
|
||||||
|
if (mi.indirect) { |
||||||
|
int j; |
||||||
|
for (j=0; j < stb_arr_len(indirect); ++j) |
||||||
|
if (indirect[j] == newval) |
||||||
|
break; |
||||||
|
if (j == stb_arr_len(indirect)) |
||||||
|
stb_arr_push(indirect, newval); |
||||||
|
stb_arr_push(index, j); |
||||||
|
} else { |
||||||
|
stb_arr_push(index, newval); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// total up the new size for everything but the index table
|
||||||
|
extra_size = mi.overhead * weight; // not the actual overhead cost; a penalty to avoid excessive complexity
|
||||||
|
extra_size += 150; // per indirection
|
||||||
|
if (table_name) |
||||||
|
extra_size = 0; |
||||||
|
|
||||||
|
if (t->has_sign) { |
||||||
|
// 'packed' contains two values, which should be packed positive & negative for size
|
||||||
|
uval maxv2; |
||||||
|
for (i=0; i < stb_arr_len(packed); ++i) |
||||||
|
if (packed[i] & 0x80000000) |
||||||
|
maxv2 = stb_max(maxv2, packed[i]); |
||||||
|
else |
||||||
|
maxv = stb_max(maxv, packed[i]); |
||||||
|
maxv = stb_max(maxv, maxv2) << 1; |
||||||
|
} else { |
||||||
|
maxv = 0; |
||||||
|
for (i=0; i < stb_arr_len(packed); ++i) |
||||||
|
if (packed[i] > maxv && packed[i] != t->dont_care) |
||||||
|
maxv = packed[i]; |
||||||
|
} |
||||||
|
extra_size += stb_arr_len(packed) * (t->splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv)); |
||||||
|
if (table_name) { |
||||||
|
if (t->splittable) |
||||||
|
output_table_with_trims(table_name, "", packed, stb_arr_len(packed)); |
||||||
|
else |
||||||
|
output_table(table_name, "", packed, stb_arr_len(packed), t->has_sign, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
maxv = 0; |
||||||
|
for (i=0; i < stb_arr_len(specials); ++i) |
||||||
|
if (specials[i] > maxv) |
||||||
|
maxv = specials[i]; |
||||||
|
extra_size += stb_arr_len(specials) * size_for_max_number_aligned(maxv); |
||||||
|
if (table_name) |
||||||
|
output_table(table_name, "_default", specials, stb_arr_len(specials), 0, NULL); |
||||||
|
|
||||||
|
maxv = 0; |
||||||
|
for (i=0; i < stb_arr_len(indirect); ++i) |
||||||
|
if (indirect[i] > maxv) |
||||||
|
maxv = indirect[i]; |
||||||
|
extra_size += stb_arr_len(indirect) * size_for_max_number(maxv); |
||||||
|
|
||||||
|
if (table_name && stb_arr_len(indirect)) { |
||||||
|
if (mi.trim_end) |
||||||
|
output_table_with_trims(table_name, "_index", indirect, stb_arr_len(indirect)); |
||||||
|
else { |
||||||
|
assert(0); // this case should only trigger in very extreme circumstances
|
||||||
|
output_table(table_name, "_index", indirect, stb_arr_len(indirect), 0, NULL); |
||||||
|
} |
||||||
|
mi.trim_end = mi.special = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (table_name) |
||||||
|
printf("// above tables should be %d bytes\n", extra_size); |
||||||
|
|
||||||
|
maxv = 0; |
||||||
|
for (i=0; i < stb_arr_len(index); ++i) |
||||||
|
if (index[i] > maxv && index[i] != t->dont_care) |
||||||
|
maxv = index[i]; |
||||||
|
newtab.splittable = mi.trim_end; |
||||||
|
newtab.input_size = newtab.splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv); |
||||||
|
newtab.input = index; |
||||||
|
newtab.length = stb_arr_len(index); |
||||||
|
newtab.inherited_storage = t->inherited_storage + extra_size; |
||||||
|
newtab.fastpath = 0; |
||||||
|
newtab.depth = t->depth+1; |
||||||
|
stb_arr_free(indirect); |
||||||
|
stb_arr_free(packed); |
||||||
|
stb_arr_free(specials); |
||||||
|
|
||||||
|
return newtab; |
||||||
|
} |
||||||
|
|
||||||
|
result pack_table(table *t, size_t path, int min_storage) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
result best; |
||||||
|
best.size = t->inherited_storage + t->input_size * t->length; |
||||||
|
best.path = path; |
||||||
|
|
||||||
|
if ((int) t->inherited_storage > min_storage) { |
||||||
|
best.size = stb_max(best.size, t->inherited_storage); |
||||||
|
return best; |
||||||
|
} |
||||||
|
|
||||||
|
if (t->length <= 256 || t->depth >= 4) { |
||||||
|
//printf("%08x: %7d\n", best.path, best.size);
|
||||||
|
return best; |
||||||
|
} |
||||||
|
|
||||||
|
path <<= 7; |
||||||
|
for (i=0; i < MODECOUNT * CLUSTERSIZECOUNT; ++i) { |
||||||
|
table newtab; |
||||||
|
result r; |
||||||
|
newtab = pack_for_mode(t, i, 0); |
||||||
|
r = pack_table(&newtab, path+i+1, min_storage); |
||||||
|
if (r.size < best.size) |
||||||
|
best = r; |
||||||
|
stb_arr_free(newtab.input); |
||||||
|
//printf("Size: %6d + %6d\n", newtab.inherited_storage, newtab.input_size * newtab.length);
|
||||||
|
} |
||||||
|
return best; |
||||||
|
} |
||||||
|
|
||||||
|
int pack_table_by_modes(table *t, int *modes) |
||||||
|
{ |
||||||
|
table s = *t; |
||||||
|
while (*modes > -1) { |
||||||
|
table newtab; |
||||||
|
newtab = pack_for_mode(&s, *modes, 0); |
||||||
|
if (s.input != t->input) |
||||||
|
stb_arr_free(s.input); |
||||||
|
s = newtab; |
||||||
|
++modes; |
||||||
|
} |
||||||
|
return s.inherited_storage + s.input_size * s.length; |
||||||
|
} |
||||||
|
|
||||||
|
int strip_table(table *t, int exceptions) |
||||||
|
{ |
||||||
|
uval terminal_value; |
||||||
|
int p = t->length-1; |
||||||
|
while (t->input[p] == t->dont_care) |
||||||
|
--p; |
||||||
|
terminal_value = t->input[p]; |
||||||
|
|
||||||
|
while (p >= 0x10000) { |
||||||
|
if (t->input[p] != terminal_value && t->input[p] != t->dont_care) { |
||||||
|
if (exceptions) |
||||||
|
--exceptions; |
||||||
|
else |
||||||
|
break; |
||||||
|
} |
||||||
|
--p; |
||||||
|
} |
||||||
|
return p+1; // p is a character we must output
|
||||||
|
} |
||||||
|
|
||||||
|
void optimize_table(table *t, char *table_name) |
||||||
|
{ |
||||||
|
int modelist[3] = { 85, -1 }; |
||||||
|
int modes[8]; |
||||||
|
int num_modes = 0; |
||||||
|
int decent_size; |
||||||
|
result r; |
||||||
|
size_t path; |
||||||
|
table s; |
||||||
|
|
||||||
|
// strip tail end of table
|
||||||
|
int orig_length = t->length; |
||||||
|
int threshhold = 0xffff; |
||||||
|
int p = strip_table(t, 2); |
||||||
|
int len_saved = t->length - p; |
||||||
|
if (len_saved >= threshhold) { |
||||||
|
t->length = p; |
||||||
|
while (p > 0x10000) { |
||||||
|
p = strip_table(t, 0); |
||||||
|
len_saved = t->length - p; |
||||||
|
if (len_saved < 0x10000) |
||||||
|
break; |
||||||
|
len_saved = orig_length - p; |
||||||
|
if (len_saved < threshhold) |
||||||
|
break; |
||||||
|
threshhold *= 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
t->depth = 1; |
||||||
|
|
||||||
|
|
||||||
|
// find size of table if we use path 86
|
||||||
|
decent_size = pack_table_by_modes(t, modelist); |
||||||
|
|
||||||
|
|
||||||
|
#if 1 |
||||||
|
// find best packing of remainder of table by exploring tree of packings
|
||||||
|
r = pack_table(t, 0, decent_size); |
||||||
|
// use the computed 'path' to evaluate and output tree
|
||||||
|
path = r.path; |
||||||
|
#else |
||||||
|
path = 86;//90;//132097;
|
||||||
|
#endif |
||||||
|
|
||||||
|
while (path) { |
||||||
|
modes[num_modes++] = (path & 127) - 1; |
||||||
|
path >>= 7; |
||||||
|
} |
||||||
|
|
||||||
|
printf("// modes: %d\n", r.path); |
||||||
|
s = *t; |
||||||
|
while (num_modes > 0) { |
||||||
|
char name[256]; |
||||||
|
sprintf(name, "%s_%d", table_name, num_modes+1); |
||||||
|
--num_modes; |
||||||
|
s = pack_for_mode(&s, modes[num_modes], name); |
||||||
|
} |
||||||
|
// output the final table as-is
|
||||||
|
if (s.splittable) |
||||||
|
output_table_with_trims(table_name, "_1", s.input, s.length); |
||||||
|
else |
||||||
|
output_table(table_name, "_1", s.input, s.length, 0, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
uval unicode_table[0x110000]; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
uval lo,hi; |
||||||
|
} char_range; |
||||||
|
|
||||||
|
char_range get_range(char *str) |
||||||
|
{ |
||||||
|
char_range cr; |
||||||
|
char *p; |
||||||
|
cr.lo = strtol(str, &p, 16); |
||||||
|
p = stb_skipwhite(p); |
||||||
|
if (*p == '.') |
||||||
|
cr.hi = strtol(p+2, NULL, 16); |
||||||
|
else |
||||||
|
cr.hi = cr.lo; |
||||||
|
return cr; |
||||||
|
} |
||||||
|
|
||||||
|
char *skip_semi(char *s, int count) |
||||||
|
{ |
||||||
|
while (count) { |
||||||
|
s = strchr(s, ';'); |
||||||
|
assert(s != NULL); |
||||||
|
++s; |
||||||
|
--count; |
||||||
|
} |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char **argv) |
||||||
|
{ |
||||||
|
table t; |
||||||
|
uval maxv=0; |
||||||
|
int i,n=0; |
||||||
|
char **s = stb_stringfile("../../data/UnicodeData.txt", &n); |
||||||
|
assert(s); |
||||||
|
for (i=0; i < n; ++i) { |
||||||
|
if (s[i][0] == '#' || s[i][0] == '\n' || s[i][0] == 0) |
||||||
|
; |
||||||
|
else { |
||||||
|
char_range cr = get_range(s[i]); |
||||||
|
char *t = skip_semi(s[i], 13); |
||||||
|
uval j, v; |
||||||
|
if (*t == ';' || *t == '\n' || *t == 0) |
||||||
|
v = 0; |
||||||
|
else { |
||||||
|
v = strtol(t, NULL, 16); |
||||||
|
if (v < 65536) { |
||||||
|
maxv = stb_max(v, maxv); |
||||||
|
for (j=cr.lo; j <= cr.hi; ++j) { |
||||||
|
unicode_table[j] = v; |
||||||
|
//printf("%06x => %06x\n", j, v);
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
t.depth = 0; |
||||||
|
t.dont_care = UVAL_DONT_CARE_DEFAULT; |
||||||
|
t.fallback = 0; |
||||||
|
t.fastpath = 256; |
||||||
|
t.inherited_storage = 0; |
||||||
|
t.has_sign = 0; |
||||||
|
t.splittable = 0; |
||||||
|
t.input = unicode_table; |
||||||
|
t.input_size = size_for_max_number(maxv); |
||||||
|
t.length = 0x110000; |
||||||
|
t.replace_fallback_with_codepoint = 1; |
||||||
|
|
||||||
|
optimize_table(&t, "stbu_upppercase"); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
# Microsoft Developer Studio Project File - Name="unicode" - Package Owner=<4> |
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00 |
||||||
|
# ** DO NOT EDIT ** |
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Console Application" 0x0103 |
||||||
|
|
||||||
|
CFG=unicode - Win32 Debug |
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE, |
||||||
|
!MESSAGE use the Export Makefile command and run |
||||||
|
!MESSAGE |
||||||
|
!MESSAGE NMAKE /f "unicode.mak". |
||||||
|
!MESSAGE |
||||||
|
!MESSAGE You can specify a configuration when running NMAKE |
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example: |
||||||
|
!MESSAGE |
||||||
|
!MESSAGE NMAKE /f "unicode.mak" CFG="unicode - Win32 Debug" |
||||||
|
!MESSAGE |
||||||
|
!MESSAGE Possible choices for configuration are: |
||||||
|
!MESSAGE |
||||||
|
!MESSAGE "unicode - Win32 Release" (based on "Win32 (x86) Console Application") |
||||||
|
!MESSAGE "unicode - Win32 Debug" (based on "Win32 (x86) Console Application") |
||||||
|
!MESSAGE |
||||||
|
|
||||||
|
# Begin Project |
||||||
|
# PROP AllowPerConfigDependencies 0 |
||||||
|
# PROP Scc_ProjName "" |
||||||
|
# PROP Scc_LocalPath "" |
||||||
|
CPP=cl.exe |
||||||
|
RSC=rc.exe |
||||||
|
|
||||||
|
!IF "$(CFG)" == "unicode - Win32 Release" |
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0 |
||||||
|
# PROP BASE Use_Debug_Libraries 0 |
||||||
|
# PROP BASE Output_Dir "Release" |
||||||
|
# PROP BASE Intermediate_Dir "Release" |
||||||
|
# PROP BASE Target_Dir "" |
||||||
|
# PROP Use_MFC 0 |
||||||
|
# PROP Use_Debug_Libraries 0 |
||||||
|
# PROP Output_Dir "Release" |
||||||
|
# PROP Intermediate_Dir "Release" |
||||||
|
# PROP Target_Dir "" |
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c |
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c |
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG" |
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG" |
||||||
|
BSC32=bscmake.exe |
||||||
|
# ADD BASE BSC32 /nologo |
||||||
|
# ADD BSC32 /nologo |
||||||
|
LINK32=link.exe |
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 |
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 |
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "unicode - Win32 Debug" |
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0 |
||||||
|
# PROP BASE Use_Debug_Libraries 1 |
||||||
|
# PROP BASE Output_Dir "Debug" |
||||||
|
# PROP BASE Intermediate_Dir "Debug" |
||||||
|
# PROP BASE Target_Dir "" |
||||||
|
# PROP Use_MFC 0 |
||||||
|
# PROP Use_Debug_Libraries 1 |
||||||
|
# PROP Output_Dir "Debug" |
||||||
|
# PROP Intermediate_Dir "Debug" |
||||||
|
# PROP Target_Dir "" |
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c |
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c |
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG" |
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG" |
||||||
|
BSC32=bscmake.exe |
||||||
|
# ADD BASE BSC32 /nologo |
||||||
|
# ADD BSC32 /nologo |
||||||
|
LINK32=link.exe |
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept |
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept |
||||||
|
|
||||||
|
!ENDIF |
||||||
|
|
||||||
|
# Begin Target |
||||||
|
|
||||||
|
# Name "unicode - Win32 Release" |
||||||
|
# Name "unicode - Win32 Debug" |
||||||
|
# Begin Source File |
||||||
|
|
||||||
|
SOURCE=..\unicode.c |
||||||
|
# End Source File |
||||||
|
# End Target |
||||||
|
# End Project |
Loading…
Reference in New Issue