📄 terminal.c
字号:
assert(runlen >= 2 && runlen <= 129);
b->data[runpos] = runlen + 0x80 - 2;
hdrpos = b->len;
hdrsize = 0;
add(b, 0);
/* And ensure this run doesn't interfere with the next. */
prevlen = prevpos = 0;
prev2 = FALSE;
continue;
} else {
/*
* Just flag that the previous two literals were
* identical, in case we find a third identical one
* we want to turn into a run.
*/
prev2 = TRUE;
prevlen = thislen;
prevpos = thispos;
}
} else {
prev2 = FALSE;
prevlen = thislen;
prevpos = thispos;
}
/*
* This character isn't (yet) part of a run. Add it to
* hdrsize.
*/
hdrsize++;
if (hdrsize == 128) {
b->data[hdrpos] = hdrsize - 1;
hdrpos = b->len;
hdrsize = 0;
add(b, 0);
prevlen = prevpos = 0;
prev2 = FALSE;
}
}
/*
* Clean up.
*/
if (hdrsize > 0) {
assert(hdrsize <= 128);
b->data[hdrpos] = hdrsize - 1;
} else {
b->len = hdrpos;
}
}
static void makeliteral_chr(struct buf *b, termchar *c, unsigned long *state)
{
/*
* My encoding for characters is UTF-8-like, in that it stores
* 7-bit ASCII in one byte and uses high-bit-set bytes as
* introducers to indicate a longer sequence. However, it's
* unlike UTF-8 in that it doesn't need to be able to
* resynchronise, and therefore I don't want to waste two bits
* per byte on having recognisable continuation characters.
* Also I don't want to rule out the possibility that I may one
* day use values 0x80000000-0xFFFFFFFF for interesting
* purposes, so unlike UTF-8 I need a full 32-bit range.
* Accordingly, here is my encoding:
*
* 00000000-0000007F: 0xxxxxxx (but see below)
* 00000080-00003FFF: 10xxxxxx xxxxxxxx
* 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
* 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
* 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
*
* (`Z' is like `x' but is always going to be zero since the
* values I'm encoding don't go above 2^32. In principle the
* five-byte form of the encoding could extend to 2^35, and
* there could be six-, seven-, eight- and nine-byte forms as
* well to allow up to 64-bit values to be encoded. But that's
* completely unnecessary for these purposes!)
*
* The encoding as written above would be very simple, except
* that 7-bit ASCII can occur in several different ways in the
* terminal data; sometimes it crops up in the D800 page
* (CSET_ASCII) but at other times it's in the 0000 page (real
* Unicode). Therefore, this encoding is actually _stateful_:
* the one-byte encoding of 00-7F actually indicates `reuse the
* upper three bytes of the last character', and to encode an
* absolute value of 00-7F you need to use the two-byte form
* instead.
*/
if ((c->chr & ~0x7F) == *state) {
add(b, (unsigned char)(c->chr & 0x7F));
} else if (c->chr < 0x4000) {
add(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80));
add(b, (unsigned char)(c->chr & 0xFF));
} else if (c->chr < 0x200000) {
add(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0));
add(b, (unsigned char)((c->chr >> 8) & 0xFF));
add(b, (unsigned char)(c->chr & 0xFF));
} else if (c->chr < 0x10000000) {
add(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0));
add(b, (unsigned char)((c->chr >> 16) & 0xFF));
add(b, (unsigned char)((c->chr >> 8) & 0xFF));
add(b, (unsigned char)(c->chr & 0xFF));
} else {
add(b, 0xF0);
add(b, (unsigned char)((c->chr >> 24) & 0xFF));
add(b, (unsigned char)((c->chr >> 16) & 0xFF));
add(b, (unsigned char)((c->chr >> 8) & 0xFF));
add(b, (unsigned char)(c->chr & 0xFF));
}
*state = c->chr & ~0xFF;
}
static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state)
{
/*
* My encoding for attributes is 16-bit-granular and assumes
* that the top bit of the word is never required. I either
* store a two-byte value with the top bit clear (indicating
* just that value), or a four-byte value with the top bit set
* (indicating the same value with its top bit clear).
*
* However, first I permute the bits of the attribute value, so
* that the eight bits of colour (four in each of fg and bg)
* which are never non-zero unless xterm 256-colour mode is in
* use are placed higher up the word than everything else. This
* ensures that attribute values remain 16-bit _unless_ the
* user uses extended colour.
*/
unsigned attr, colourbits;
attr = c->attr;
assert(ATTR_BGSHIFT > ATTR_FGSHIFT);
colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF;
colourbits <<= 4;
colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF;
attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) |
(attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) |
(attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
attr |= (colourbits << (32-9));
if (attr < 0x8000) {
add(b, (unsigned char)((attr >> 8) & 0xFF));
add(b, (unsigned char)(attr & 0xFF));
} else {
add(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80));
add(b, (unsigned char)((attr >> 16) & 0xFF));
add(b, (unsigned char)((attr >> 8) & 0xFF));
add(b, (unsigned char)(attr & 0xFF));
}
}
static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)
{
/*
* For combining characters, I just encode a bunch of ordinary
* chars using makeliteral_chr, and terminate with a \0
* character (which I know won't come up as a combining char
* itself).
*
* I don't use the stateful encoding in makeliteral_chr.
*/
unsigned long zstate;
termchar z;
while (c->cc_next) {
c += c->cc_next;
assert(c->chr != 0);
zstate = 0;
makeliteral_chr(b, c, &zstate);
}
z.chr = 0;
zstate = 0;
makeliteral_chr(b, &z, &zstate);
}
static termline *decompressline(unsigned char *data, int *bytes_used);
static unsigned char *compressline(termline *ldata)
{
struct buf buffer = { NULL, 0, 0 }, *b = &buffer;
/*
* First, store the column count, 7 bits at a time, least
* significant `digit' first, with the high bit set on all but
* the last.
*/
{
int n = ldata->cols;
while (n >= 128) {
add(b, (unsigned char)((n & 0x7F) | 0x80));
n >>= 7;
}
add(b, (unsigned char)(n));
}
/*
* Next store the lattrs; same principle.
*/
{
int n = ldata->lattr;
while (n >= 128) {
add(b, (unsigned char)((n & 0x7F) | 0x80));
n >>= 7;
}
add(b, (unsigned char)(n));
}
/*
* Now we store a sequence of separate run-length encoded
* fragments, each containing exactly as many symbols as there
* are columns in the ldata.
*
* All of these have a common basic format:
*
* - a byte 00-7F indicates that X+1 literals follow it
* - a byte 80-FF indicates that a single literal follows it
* and expects to be repeated (X-0x80)+2 times.
*
* The format of the `literals' varies between the fragments.
*/
makerle(b, ldata, makeliteral_chr);
makerle(b, ldata, makeliteral_attr);
makerle(b, ldata, makeliteral_cc);
/*
* Diagnostics: ensure that the compressed data really does
* decompress to the right thing.
*
* This is a bit performance-heavy for production code.
*/
#ifdef TERM_CC_DIAGS
#ifndef CHECK_SB_COMPRESSION
{
int dused;
termline *dcl;
int i;
#ifdef DIAGNOSTIC_SB_COMPRESSION
for (i = 0; i < b->len; i++) {
printf(" %02x ", b->data[i]);
}
printf("\n");
#endif
dcl = decompressline(b->data, &dused);
assert(b->len == dused);
assert(ldata->cols == dcl->cols);
assert(ldata->lattr == dcl->lattr);
for (i = 0; i < ldata->cols; i++)
assert(termchars_equal(&ldata->chars[i], &dcl->chars[i]));
#ifdef DIAGNOSTIC_SB_COMPRESSION
printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n",
ldata->cols, 4 * ldata->cols, dused,
(double)dused / (4 * ldata->cols));
#endif
freeline(dcl);
}
#endif
#endif /* TERM_CC_DIAGS */
/*
* Trim the allocated memory so we don't waste any, and return.
*/
return sresize(b->data, b->len, unsigned char);
}
static void readrle(struct buf *b, termline *ldata,
void (*readliteral)(struct buf *b, termchar *c,
termline *ldata, unsigned long *state))
{
int n = 0;
unsigned long state = 0;
while (n < ldata->cols) {
int hdr = get(b);
if (hdr >= 0x80) {
/* A run. */
int pos = b->len, count = hdr + 2 - 0x80;
while (count--) {
assert(n < ldata->cols);
b->len = pos;
readliteral(b, ldata->chars + n, ldata, &state);
n++;
}
} else {
/* Just a sequence of consecutive literals. */
int count = hdr + 1;
while (count--) {
assert(n < ldata->cols);
readliteral(b, ldata->chars + n, ldata, &state);
n++;
}
}
}
assert(n == ldata->cols);
}
static void readliteral_chr(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
int byte;
/*
* 00000000-0000007F: 0xxxxxxx
* 00000080-00003FFF: 10xxxxxx xxxxxxxx
* 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
* 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
* 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
*/
byte = get(b);
if (byte < 0x80) {
c->chr = byte | *state;
} else if (byte < 0xC0) {
c->chr = (byte &~ 0xC0) << 8;
c->chr |= get(b);
} else if (byte < 0xE0) {
c->chr = (byte &~ 0xE0) << 16;
c->chr |= get(b) << 8;
c->chr |= get(b);
} else if (byte < 0xF0) {
c->chr = (byte &~ 0xF0) << 24;
c->chr |= get(b) << 16;
c->chr |= get(b) << 8;
c->chr |= get(b);
} else {
assert(byte == 0xF0);
c->chr = get(b) << 24;
c->chr |= get(b) << 16;
c->chr |= get(b) << 8;
c->chr |= get(b);
}
*state = c->chr & ~0xFF;
}
static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
unsigned val, attr, colourbits;
val = get(b) << 8;
val |= get(b);
if (val >= 0x8000) {
val &= ~0x8000;
val <<= 16;
val |= get(b) << 8;
val |= get(b);
}
colourbits = (val >> (32-9)) & 0xFF;
attr = (val & ((1<<(32-9))-1));
attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) |
(attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) |
(attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4);
attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4);
c->attr = attr;
}
static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
termchar n;
unsigned long zstate;
int x = c - ldata->chars;
c->cc_next = 0;
while (1) {
zstate = 0;
readliteral_chr(b, &n, ldata, &zstate);
if (!n.chr)
break;
add_cc(ldata, x, n.chr);
}
}
static termline *decompressline(unsigned char *data, int *bytes_used)
{
int ncols, byte, shift;
struct buf buffer, *b = &buffer;
termline *ldata;
b->data = data;
b->len = 0;
/*
* First read in the column count.
*/
ncols = shift = 0;
do {
byte = get(b);
ncols |= (byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
/*
* Now create the output termline.
*/
ldata = snew(termline);
ldata->chars = snewn(ncols, termchar);
ldata->cols = ldata->size = ncols;
ldata->temporary = TRUE;
ldata->cc_free = 0;
/*
* We must set all the cc pointers in ldata->chars to 0 right
* now, so that cc diagnostics that verify the integrity of the
* whole line will make sense while we're in the middle of
* building it up.
*/
{
int i;
for (i = 0; i < ldata->cols; i++)
ldata->chars[i].cc_next = 0;
}
/*
* Now read in the lattr.
*/
ldata->lattr = shift = 0;
do {
byte = get(b);
ldata->lattr |= (byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
/*
* Now we read in each of the RLE streams in turn.
*/
readrle(b, ldata, readliteral_chr);
readrle(b, ldata, readliteral_attr);
readrle(b, ldata, readliteral_cc);
/* Return the number of bytes read, for diagnostic purposes. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -