Squashed 'external/stb/stb/' content from commit c39c7023e
git-subtree-dir: external/stb/stb git-subtree-split: c39c7023ebb833ce099750fe35509aca5662695e
This commit is contained in:
135
tools/README.footer.md
Normal file
135
tools/README.footer.md
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
#### What's the license?
|
||||
|
||||
These libraries are in the public domain. You can do anything you
|
||||
want with them. You have no legal obligation
|
||||
to do anything else, although I appreciate attribution.
|
||||
|
||||
They are also licensed under the MIT open source license, if you have lawyers
|
||||
who are unhappy with public domain. Every source file includes an explicit
|
||||
dual-license for you to choose from.
|
||||
|
||||
#### How do I use these libraries?
|
||||
|
||||
The idea behind single-header file libraries is that they're easy to distribute and deploy
|
||||
because all the code is contained in a single file. By default, the .h files in here act as
|
||||
their own header files, i.e. they declare the functions contained in the file but don't
|
||||
actually result in any code getting compiled.
|
||||
|
||||
So in addition, you should select _exactly one_ C/C++ source file that actually instantiates
|
||||
the code, preferably a file you're not editing frequently. This file should define a
|
||||
specific macro (this is documented per-library) to actually enable the function definitions.
|
||||
For example, to use stb_image, you should have exactly one C/C++ file that doesn't
|
||||
include stb_image.h regularly, but instead does
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
The right macro to define is pointed out right at the top of each of these libraries.
|
||||
|
||||
#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
|
||||
|
||||
[Yes.](https://github.com/nothings/single_file_libs)
|
||||
|
||||
#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
|
||||
|
||||
No, because it's public domain you can freely relicense it to whatever license your new
|
||||
library wants to be.
|
||||
|
||||
#### What's the deal with SSE support in GCC-based compilers?
|
||||
|
||||
stb_image will either use SSE2 (if you compile with -msse2) or
|
||||
will not use any SIMD at all, rather than trying to detect the
|
||||
processor at runtime and handle it correctly. As I understand it,
|
||||
the approved path in GCC for runtime-detection require
|
||||
you to use multiple source files, one for each CPU configuration.
|
||||
Because stb_image is a header-file library that compiles in only
|
||||
one source file, there's no approved way to build both an
|
||||
SSE-enabled and a non-SSE-enabled variation.
|
||||
|
||||
While we've tried to work around it, we've had multiple issues over
|
||||
the years due to specific versions of gcc breaking what we're doing,
|
||||
so we've given up on it. See https://github.com/nothings/stb/issues/280
|
||||
and https://github.com/nothings/stb/issues/410 for examples.
|
||||
|
||||
#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
|
||||
|
||||
Generally they're only better in that they're easier to integrate,
|
||||
easier to use, and easier to release (single file; good API; no
|
||||
attribution requirement). They may be less featureful, slower,
|
||||
and/or use more memory. If you're already using an equivalent
|
||||
library, there's probably no good reason to switch.
|
||||
|
||||
#### Can I link directly to the table of stb libraries?
|
||||
|
||||
You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
|
||||
|
||||
#### Why do you list "lines of code"? It's a terrible metric.
|
||||
|
||||
Just to give you some idea of the internal complexity of the library,
|
||||
to help you manage your expectations, or to let you know what you're
|
||||
getting into. While not all the libraries are written in the same
|
||||
style, they're certainly similar styles, and so comparisons between
|
||||
the libraries are probably still meaningful.
|
||||
|
||||
Note though that the lines do include both the implementation, the
|
||||
part that corresponds to a header file, and the documentation.
|
||||
|
||||
#### Why single-file headers?
|
||||
|
||||
Windows doesn't have standard directories where libraries
|
||||
live. That makes deploying libraries in Windows a lot more
|
||||
painful than open source developers on Unix-derivates generally
|
||||
realize. (It also makes library dependencies a lot worse in Windows.)
|
||||
|
||||
There's also a common problem in Windows where a library was built
|
||||
against a different version of the runtime library, which causes
|
||||
link conflicts and confusion. Shipping the libs as headers means
|
||||
you normally just compile them straight into your project without
|
||||
making libraries, thus sidestepping that problem.
|
||||
|
||||
Making them a single file makes it very easy to just
|
||||
drop them into a project that needs them. (Of course you can
|
||||
still put them in a proper shared library tree if you want.)
|
||||
|
||||
Why not two files, one a header and one an implementation?
|
||||
The difference between 10 files and 9 files is not a big deal,
|
||||
but the difference between 2 files and 1 file is a big deal.
|
||||
You don't need to zip or tar the files up, you don't have to
|
||||
remember to attach *two* files, etc.
|
||||
|
||||
#### Why "stb"? Is this something to do with Set-Top Boxes?
|
||||
|
||||
No, they are just the initials for my name, Sean T. Barrett.
|
||||
This was not chosen out of egomania, but as a moderately sane
|
||||
way of namespacing the filenames and source function names.
|
||||
|
||||
#### Will you add more image types to stb_image.h?
|
||||
|
||||
No. As stb_image use has grown, it has become more important
|
||||
for us to focus on security of the codebase. Adding new image
|
||||
formats increases the amount of code we need to secure, so it
|
||||
is no longer worth adding new formats.
|
||||
|
||||
#### Do you have any advice on how to create my own single-file library?
|
||||
|
||||
Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
|
||||
|
||||
#### Why public domain?
|
||||
|
||||
I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons.
|
||||
Some of them are listed here:
|
||||
https://github.com/nothings/stb/blob/master/docs/why_public_domain.md
|
||||
|
||||
#### Why C?
|
||||
|
||||
Primarily, because I use C, not C++. But it does also make it easier
|
||||
for other people to use them from other languages.
|
||||
|
||||
#### Why not C99? stdint.h, declare-anywhere, etc.
|
||||
|
||||
I still use MSVC 6 (1998) as my IDE because it has better human factors
|
||||
for me than later versions of MSVC.
|
20
tools/README.header.md
Normal file
20
tools/README.header.md
Normal file
@ -0,0 +1,20 @@
|
||||
stb
|
||||
===
|
||||
|
||||
single-file public domain (or MIT licensed) libraries for C/C++
|
||||
|
||||
Noteworthy:
|
||||
|
||||
* image loader: [stb_image.h](stb_image.h)
|
||||
* image writer: [stb_image_write.h](stb_image_write.h)
|
||||
* image resizer: [stb_image_resize.h](stb_image_resize.h)
|
||||
* font text rasterizer: [stb_truetype.h](stb_truetype.h)
|
||||
* typesafe containers: [stb_ds.h](stb_ds.h)
|
||||
|
||||
Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, stb_image_resize
|
||||
by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts.
|
||||
|
||||
<a name="stb_libs"></a>
|
||||
|
||||
library | lastest version | category | LoC | description
|
||||
--------------------- | ---- | -------- | --- | --------------------------------
|
21
tools/README.list
Normal file
21
tools/README.list
Normal file
@ -0,0 +1,21 @@
|
||||
stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output
|
||||
stb_hexwave.h | audio | audio waveform synthesizer
|
||||
stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
|
||||
stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts
|
||||
stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP
|
||||
stb_image_resize.h | graphics | resize images larger/smaller with good quality
|
||||
stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality
|
||||
stb_perlin.h | graphics | perlin's revised simplex noise w/ different seeds
|
||||
stb_ds.h | utility | typesafe dynamic array and hash tables for C, will compile in C++
|
||||
stb_sprintf.h | utility | fast sprintf, snprintf for C/C++
|
||||
stb_textedit.h | user interface | guts of a text editor for games etc implementing them from scratch
|
||||
stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features
|
||||
stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
|
||||
stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
|
||||
stb_tilemap_editor.h | game dev | embeddable tilemap editor
|
||||
stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator
|
||||
stb_c_lexer.h | parsing | simplify writing parsers for C-like languages
|
||||
stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide"
|
||||
stb_connected_components.h | misc | incrementally compute reachability on grids
|
||||
stb_leakcheck.h | misc | quick-and-dirty malloc/free leak-checking
|
||||
stb_include.h | misc | implement recursive #include support, particularly for GLSL
|
137
tools/build_matrix.c
Normal file
137
tools/build_matrix.c
Normal file
@ -0,0 +1,137 @@
|
||||
#define STB_DEFINE
|
||||
#include "stb.h"
|
||||
|
||||
// true if no error
|
||||
int run_command(char *batch_file, char *command)
|
||||
{
|
||||
char buffer[4096];
|
||||
if (batch_file[0]) {
|
||||
sprintf(buffer, "%s && %s", batch_file, command);
|
||||
return system(buffer) == 0;
|
||||
} else {
|
||||
return system(command) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *compiler_name;
|
||||
char *batchfile;
|
||||
char *objdir;
|
||||
char *compiler;
|
||||
char *args;
|
||||
char *link;
|
||||
} compiler_info;
|
||||
|
||||
compiler_info *compilers;
|
||||
char *shared_args;
|
||||
char *shared_link;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *filelist;
|
||||
} project_info;
|
||||
|
||||
project_info *projects;
|
||||
|
||||
enum { NONE, IN_COMPILERS, IN_ARGS, IN_PROJECTS, IN_LINK };
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int state = NONE;
|
||||
int i,j,n;
|
||||
char **line;
|
||||
if (argc != 2) stb_fatal("Usage: stb_build_matrix {build-file}\n");
|
||||
line = stb_stringfile(argv[1], &n);
|
||||
if (line == 0) stb_fatal("Couldn't open file '%s'\n", argv[1]);
|
||||
|
||||
for (i=0; i < n; ++i) {
|
||||
char *p = stb_trimwhite(line[i]);
|
||||
if (p[0] == 0) continue;
|
||||
else if (p[0] == '#') continue;
|
||||
else if (0 == stricmp(p, "[compilers]")) { state = IN_COMPILERS; }
|
||||
else if (0 == stricmp(p, "[args]" )) { state = IN_ARGS ; shared_args = NULL; }
|
||||
else if (0 == stricmp(p, "[projects]" )) { state = IN_PROJECTS ; }
|
||||
else if (0 == stricmp(p, "[link]" )) { state = IN_LINK ; shared_link = NULL; }
|
||||
else {
|
||||
switch (state) {
|
||||
case NONE: stb_fatal("Invalid text outside section at line %d.", i+1);
|
||||
case IN_COMPILERS: {
|
||||
char buffer[4096];
|
||||
int count;
|
||||
compiler_info ci;
|
||||
char **tokens = stb_tokens_stripwhite(p, ",", &count), *batch;
|
||||
if (count > 3) stb_fatal("Expecting name and batch file name at line %d.", i+1);
|
||||
batch = (count==1 ? tokens[0] : tokens[1]);
|
||||
if (strlen(batch))
|
||||
sprintf(buffer, "c:\\%s.bat", batch);
|
||||
else
|
||||
strcpy(buffer, "");
|
||||
ci.compiler_name = strdup(tokens[0]);
|
||||
ci.batchfile = strdup(buffer);
|
||||
ci.compiler = count==3 ? strdup(tokens[2]) : "cl";
|
||||
if (0==strnicmp(batch, "vcvars_", 7))
|
||||
ci.objdir = strdup(stb_sprintf("vs_%s_%d", batch+7, i));
|
||||
else
|
||||
ci.objdir = strdup(stb_sprintf("%s_%d", batch, i));
|
||||
ci.args = shared_args;
|
||||
ci.link = shared_link;
|
||||
stb_arr_push(compilers, ci);
|
||||
break;
|
||||
}
|
||||
case IN_ARGS: {
|
||||
stb_arr_push(shared_args, ' ');
|
||||
for (j=0; p[j] != 0; ++j)
|
||||
stb_arr_push(shared_args, p[j]);
|
||||
break;
|
||||
}
|
||||
case IN_LINK: {
|
||||
stb_arr_push(shared_link, ' ');
|
||||
for (j=0; p[j] != 0; ++j)
|
||||
stb_arr_push(shared_link, p[j]);
|
||||
break;
|
||||
}
|
||||
case IN_PROJECTS: {
|
||||
project_info pi;
|
||||
pi.filelist = strdup(p);
|
||||
stb_arr_push(projects, pi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_mkdir("obj");
|
||||
for (j=0; j < stb_arr_len(compilers); ++j) {
|
||||
char command[4096];
|
||||
for (i=0; i < stb_arr_len(projects); ++i) {
|
||||
int r;
|
||||
_mkdir(stb_sprintf("obj/%s", compilers[j].objdir));
|
||||
if (stb_suffix(compilers[j].compiler, "cl"))
|
||||
sprintf(command, "%s %.*s %s /link %.*s",
|
||||
compilers[j].compiler,
|
||||
stb_arr_len(compilers[j].args), compilers[j].args,
|
||||
projects[i].filelist,
|
||||
stb_arr_len(compilers[j].link), compilers[j].link);
|
||||
else
|
||||
sprintf(command, "%s %.*s %s %.*s",
|
||||
compilers[j].compiler,
|
||||
stb_arr_len(compilers[j].args), compilers[j].args,
|
||||
projects[i].filelist,
|
||||
stb_arr_len(compilers[j].link), compilers[j].link);
|
||||
r = run_command(compilers[j].batchfile, command);
|
||||
stbprint("{%c== Compiler %s == Building %s}\n", r ? '$' : '!', compilers[j].compiler_name, projects[i].filelist);
|
||||
stb_copyfile("a.exe", stb_sprintf("obj/%s/a.exe", compilers[j].objdir));
|
||||
//printf("Copy: %s to %s\n", "a.exe", stb_sprintf("obj/%s/a.exe", compilers[j].objdir));
|
||||
stb_copyfile("temp.exe", stb_sprintf("obj/%s/temp.exe", compilers[j].objdir));
|
||||
system("if EXIST a.exe del /q a.exe");
|
||||
system("if EXIST temp.exe del /q temp.exe");
|
||||
system("if EXIST *.obj del /q *.obj");
|
||||
system("if EXIST *.o del /q *.o");
|
||||
if (!r)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
211
tools/easy_font_maker.c
Normal file
211
tools/easy_font_maker.c
Normal file
@ -0,0 +1,211 @@
|
||||
// This program was used to encode the data for stb_simple_font.h
|
||||
|
||||
#define STB_DEFINE
|
||||
#include "stb.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
int w,h;
|
||||
uint8 *data;
|
||||
|
||||
int last_x[2], last_y[2];
|
||||
int num_seg[2], non_empty;
|
||||
#if 0
|
||||
typedef struct
|
||||
{
|
||||
unsigned short first_segment;
|
||||
unsigned char advance;
|
||||
} chardata;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char x:4;
|
||||
unsigned char y:4;
|
||||
unsigned char len:3;
|
||||
unsigned char dir:1;
|
||||
} segment;
|
||||
|
||||
segment *segments;
|
||||
|
||||
void add_seg(int x, int y, int len, int horizontal)
|
||||
{
|
||||
segment s;
|
||||
s.x = x;
|
||||
s.y = y;
|
||||
s.len = len;
|
||||
s.dir = horizontal;
|
||||
assert(s.x == x);
|
||||
assert(s.y == y);
|
||||
assert(s.len == len);
|
||||
stb_arr_push(segments, s);
|
||||
}
|
||||
#else
|
||||
typedef struct
|
||||
{
|
||||
unsigned char first_segment:8;
|
||||
unsigned char first_v_segment:8;
|
||||
unsigned char advance:5;
|
||||
unsigned char voff:1;
|
||||
} chardata;
|
||||
|
||||
#define X_LIMIT 1
|
||||
#define LEN_LIMIT 7
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char dx:1;
|
||||
unsigned char y:4;
|
||||
unsigned char len:3;
|
||||
} segment;
|
||||
|
||||
segment *segments;
|
||||
segment *vsegments;
|
||||
|
||||
void add_seg(int x, int y, int len, int horizontal)
|
||||
{
|
||||
segment s;
|
||||
|
||||
while (x - last_x[horizontal] > X_LIMIT) {
|
||||
add_seg(last_x[horizontal] + X_LIMIT, 0, 0, horizontal);
|
||||
}
|
||||
while (len > LEN_LIMIT) {
|
||||
add_seg(x, y, LEN_LIMIT, horizontal);
|
||||
len -= LEN_LIMIT;
|
||||
x += LEN_LIMIT*horizontal;
|
||||
y += LEN_LIMIT*!horizontal;
|
||||
}
|
||||
|
||||
s.dx = x - last_x[horizontal];
|
||||
s.y = y;
|
||||
s.len = len;
|
||||
non_empty += len != 0;
|
||||
//assert(s.x == x);
|
||||
assert(s.y == y);
|
||||
assert(s.len == len);
|
||||
++num_seg[horizontal];
|
||||
if (horizontal)
|
||||
stb_arr_push(segments, s);
|
||||
else
|
||||
stb_arr_push(vsegments, s);
|
||||
last_x[horizontal] = x;
|
||||
}
|
||||
|
||||
void print_segments(segment *s)
|
||||
{
|
||||
int i, hpos;
|
||||
printf(" ");
|
||||
hpos = 4;
|
||||
for (i=0; i < stb_arr_len(s); ++i) {
|
||||
// repack for portability
|
||||
unsigned char seg = s[i].len + s[i].dx*8 + s[i].y*16;
|
||||
hpos += printf("%d,", seg);
|
||||
if (hpos > 72 && i+1 < stb_arr_len(s)) {
|
||||
hpos = 4;
|
||||
printf("\n ");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
chardata charinfo[128];
|
||||
|
||||
int parse_char(int x, chardata *c, int offset)
|
||||
{
|
||||
int start_x = x, end_x, top_y = 0, y;
|
||||
|
||||
c->first_segment = stb_arr_len(segments);
|
||||
c->first_v_segment = stb_arr_len(vsegments) - offset;
|
||||
assert(c->first_segment == stb_arr_len(segments));
|
||||
assert(c->first_v_segment + offset == stb_arr_len(vsegments));
|
||||
|
||||
// find advance distance
|
||||
end_x = x+1;
|
||||
while (data[end_x*3] == 255)
|
||||
++end_x;
|
||||
c->advance = end_x - start_x + 1;
|
||||
|
||||
last_x[0] = last_x[1] = 0;
|
||||
last_y[0] = last_y[1] = 0;
|
||||
|
||||
for (y=2; y < h; ++y) {
|
||||
for (x=start_x; x < end_x; ++x) {
|
||||
if (data[y*3*w+x*3+1] < 255) {
|
||||
top_y = y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (top_y)
|
||||
break;
|
||||
}
|
||||
c->voff = top_y > 2;
|
||||
if (top_y > 2)
|
||||
top_y = 3;
|
||||
|
||||
for (x=start_x; x < end_x; ++x) {
|
||||
int y;
|
||||
for (y=2; y < h; ++y) {
|
||||
if (data[y*3*w+x*3+1] < 255) {
|
||||
if (data[y*3*w+x*3+0] == 255) { // red
|
||||
int len=0;
|
||||
while (y+len < h && data[(y+len)*3*w+x*3+0] == 255 && data[(y+len)*3*w+x*3+1] == 0) {
|
||||
data[(y+len)*3*w+x*3+0] = 0;
|
||||
++len;
|
||||
}
|
||||
add_seg(x-start_x,y-top_y,len,0);
|
||||
}
|
||||
if (data[y*3*w+x*3+2] == 255) { // blue
|
||||
int len=0;
|
||||
while (x+len < end_x && data[y*3*w+(x+len)*3+2] == 255 && data[y*3*w+(x+len)*3+1] == 0) {
|
||||
data[y*3*w+(x+len)*3+2] = 0;
|
||||
++len;
|
||||
}
|
||||
add_seg(x-start_x,y-top_y,len,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return end_x;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, x=0;
|
||||
data = stbi_load("easy_font_raw.png", &w, &h, 0, 3);
|
||||
for (c=32; c < 127; ++c) {
|
||||
x = parse_char(x, &charinfo[c], 0);
|
||||
printf("%3d -- %3d %3d\n", c, charinfo[c].first_segment, charinfo[c].first_v_segment);
|
||||
}
|
||||
printf("===\n");
|
||||
printf("%d %d %d\n", num_seg[0], num_seg[1], non_empty);
|
||||
printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments));
|
||||
printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments) + sizeof(segments[0]) * stb_arr_len(vsegments) + sizeof(charinfo[32])*95);
|
||||
|
||||
printf("struct {\n"
|
||||
" unsigned char advance;\n"
|
||||
" unsigned char h_seg;\n"
|
||||
" unsigned char v_seg;\n"
|
||||
"} stb_easy_font_charinfo[96] = {\n");
|
||||
charinfo[c].first_segment = stb_arr_len(segments);
|
||||
charinfo[c].first_v_segment = stb_arr_len(vsegments);
|
||||
for (c=32; c < 128; ++c) {
|
||||
if ((c & 3) == 0) printf(" ");
|
||||
printf("{ %2d,%3d,%3d },",
|
||||
charinfo[c].advance + 16*charinfo[c].voff,
|
||||
charinfo[c].first_segment,
|
||||
charinfo[c].first_v_segment);
|
||||
if ((c & 3) == 3) printf("\n"); else printf(" ");
|
||||
}
|
||||
printf("};\n\n");
|
||||
|
||||
printf("unsigned char stb_easy_font_hseg[%d] = {\n", stb_arr_len(segments));
|
||||
print_segments(segments);
|
||||
printf("};\n\n");
|
||||
|
||||
printf("unsigned char stb_easy_font_vseg[%d] = {\n", stb_arr_len(vsegments));
|
||||
print_segments(vsegments);
|
||||
printf("};\n");
|
||||
return 0;
|
||||
}
|
65
tools/make_readme.c
Normal file
65
tools/make_readme.c
Normal file
@ -0,0 +1,65 @@
|
||||
#define STB_DEFINE
|
||||
#include "../stb.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int hlen, flen, listlen, total_lines = 0;
|
||||
char *header = stb_file("README.header.md", &hlen); // stb_file - read file into malloc()ed buffer
|
||||
char *footer = stb_file("README.footer.md", &flen); // stb_file - read file into malloc()ed buffer
|
||||
char **list = stb_stringfile("README.list", &listlen); // stb_stringfile - read file lines into malloced array of strings
|
||||
|
||||
FILE *f = fopen("../README.md", "wb");
|
||||
|
||||
fprintf(f, "<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->\r\n\r\n");
|
||||
fwrite(header, 1, hlen, f);
|
||||
|
||||
for (i=0; i < listlen; ++i) {
|
||||
int num,j;
|
||||
char **tokens = stb_tokens_stripwhite(list[i], "|", &num); // stb_tokens -- tokenize string into malloced array of strings
|
||||
int num_lines;
|
||||
char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines);
|
||||
char *s1, *s2,*s3;
|
||||
if (lines == NULL) stb_fatal("Couldn't open '%s'", tokens[0]);
|
||||
s1 = strchr(lines[0], '-');
|
||||
if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit
|
||||
s2 = strchr(s1+2, '-');
|
||||
if (!s2) stb_fatal("Couldn't find '-' after version number in %s", tokens[0]); // stb_fatal -- print error message & exit
|
||||
*s2 = 0;
|
||||
s1 += 1;
|
||||
s1 = stb_trimwhite(s1); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace
|
||||
if (*s1 == 'v') ++s1;
|
||||
s3 = tokens[0];
|
||||
stb_trimwhite(s3);
|
||||
fprintf(f, "**[");
|
||||
if (strlen(s3) < 21) {
|
||||
fprintf(f, "%s", tokens[0]);
|
||||
} else {
|
||||
char buffer[256];
|
||||
strncpy(buffer, s3, 18);
|
||||
buffer[18] = 0;
|
||||
strcat(buffer, "...");
|
||||
fprintf(f, "%s", buffer);
|
||||
}
|
||||
fprintf(f, "](%s)**", tokens[0]);
|
||||
fprintf(f, " | %s", s1);
|
||||
s1 = stb_trimwhite(tokens[1]); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace
|
||||
s2 = stb_dupreplace(s1, " ", " "); // stb_dupreplace -- search & replace string and malloc result
|
||||
fprintf(f, " | %s", s2);
|
||||
free(s2);
|
||||
fprintf(f, " | %d", num_lines);
|
||||
total_lines += num_lines;
|
||||
for (j=2; j < num; ++j)
|
||||
fprintf(f, " | %s", tokens[j]);
|
||||
fprintf(f, "\r\n");
|
||||
}
|
||||
|
||||
fprintf(f, "\r\n");
|
||||
fprintf(f, "Total libraries: %d\r\n", listlen);
|
||||
fprintf(f, "Total lines of C code: %d\r\n\r\n", total_lines);
|
||||
|
||||
fwrite(footer, 1, flen, f);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
97
tools/make_readme.dsp
Normal file
97
tools/make_readme.dsp
Normal file
@ -0,0 +1,97 @@
|
||||
# Microsoft Developer Studio Project File - Name="make_readme" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=make_readme - 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 "make_readme.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 "make_readme.mak" CFG="make_readme - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "make_readme - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "make_readme - 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)" == "make_readme - 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)" == "make_readme - 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\make_readme"
|
||||
# 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" /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# 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 "make_readme - Win32 Release"
|
||||
# Name "make_readme - Win32 Debug"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\make_readme.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\README.header.md
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\README.list
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
1
tools/mr.bat
Normal file
1
tools/mr.bat
Normal file
@ -0,0 +1 @@
|
||||
debug\make_readme
|
32
tools/trailing_whitespace.c
Normal file
32
tools/trailing_whitespace.c
Normal file
@ -0,0 +1,32 @@
|
||||
#define STB_DEFINE
|
||||
#include "stb.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
for (i=1; i < argc; ++i) {
|
||||
int len;
|
||||
FILE *f;
|
||||
char *s = stb_file(argv[i], &len);
|
||||
char *end, *src, *dest;
|
||||
if (s == NULL) {
|
||||
printf("Couldn't read file '%s'.\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
end = s + len;
|
||||
src = dest = s;
|
||||
while (src < end) {
|
||||
char *start=0;
|
||||
while (src < end && *src != '\n' && *src != '\r')
|
||||
*dest++ = *src++;
|
||||
while (dest-1 > s && (dest[-1] == ' ' || dest[-1] == '\t'))
|
||||
--dest;
|
||||
while (src < end && (*src == '\n' || *src == '\r'))
|
||||
*dest++ = *src++;
|
||||
}
|
||||
f = fopen(argv[i], "wb");
|
||||
fwrite(s, 1, dest-s, f);
|
||||
fclose(f);
|
||||
}
|
||||
return 0;
|
||||
}
|
749
tools/unicode.c
Normal file
749
tools/unicode.c
Normal file
@ -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 specifying 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;
|
||||
}
|
88
tools/unicode/unicode.dsp
Normal file
88
tools/unicode/unicode.dsp
Normal file
@ -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
|
Reference in New Issue
Block a user