Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #70951 > unrolled thread
| Started by | luser droog <luser.droog@gmail.com> |
|---|---|
| First post | 2015-09-19 15:46 -0700 |
| Last post | 2015-09-24 18:17 -0700 |
| Articles | 5 — 3 participants |
Back to article view | Back to comp.lang.c
Data-driven template-generation and fancy macros luser droog <luser.droog@gmail.com> - 2015-09-19 15:46 -0700
Re: Data-driven template-generation and fancy macros Ike Naar <ike@iceland.freeshell.org> - 2015-09-20 20:12 +0000
Re: Data-driven template-generation and fancy macros luser droog <luser.droog@gmail.com> - 2015-09-20 15:11 -0700
Re: Data-driven template-generation and fancy macros Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-20 23:45 +0100
Re: Data-driven template-generation and fancy macros luser droog <luser.droog@gmail.com> - 2015-09-24 18:17 -0700
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2015-09-19 15:46 -0700 |
| Subject | Data-driven template-generation and fancy macros |
| Message-ID | <4b733f9c-1913-4a88-ac06-b34d104b9922@googlegroups.com> |
Back to the utf-8 code I brought up earlier, which discussion
resulted in a very nice bit of code IMO.
https://github.com/luser-dr00g/inca/blob/master/olmec/io.c
But the early version had a data-driven style that offers
definite advantages wrt adapting the protocol semantics
and adding new ranges. By defining a new range in the table,
it generates the encoder and decoder details automatically.
So the question here is (besides "losing them") what can I do
to improve the macros here and make the generators easier to
read? Would m4 probably be better for this?
"commented version" from https://groups.google.com/d/topic/comp.lang.c/ki2ar-qVLAQ/discussion
//cf. http://www.ietf.org/rfc/rfc3629.txt p.3
#include<stdlib.h>
/*
<-------- adapters ("apps-"hungarian naming scheme)
utf8_ucs4
ucs4_utf8
*/
int_least32_t *ucs4_utf8(char *str, int n);
char *utf8_ucs4(int_least32_t *ar, int n, int *an);
/* Master table macro describing the translation.
The arguments to the parameter macro are the lower bound of the UCS range for this case,
upper bound, number of corresponding encoded utf-8 bytes, followed by quadruples describing
each byte. The quadruple is the mask for the byte's header field, the value of the signature
matching the masked bits, and the mask for the value section (== logical NOT of the header
mask), and shift or number of lower bits allocated to later bytes.
*/
#define UCS_RANGE(_) \
_(0x00000000,0x0000007F,1, 0x80,0x00,0x7F,0) \
_(0x00000080,0x000007FF,2, 0xE0,0xC0,0x1F,6, 0xC0,0x80,0x3F,0) \
_(0x00000800,0x0000FFFF,3, 0xF0,0xE0,0x0F,12, 0xC0,0x80,0x3F,6, 0xC0,0x80,0x3F,0) \
_(0x00010000,0x0010FFFF,4, 0xF8,0xF0,0x07,18, 0xC0,0x80,0x3F,12, 0xC0,0x80,0x3F,6, 0xC0,0x80,0x3F,0)
/* Macros to match each of the 4 valid forms of utf-8 encoding.
If the first byte ANDed with the header mask is equal to the signature, then this
is the correct case. Decode the bytes, updating the buffer and u pointer, and
increment the p pointer so one more increment will point to the next byte.
*/
#define MATCH1(msk,sig,val,shf) \
if ((*p&msk)==sig) { *u++=*p&val; }
#define MATCH2(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1) \
if ((*p&msk0)==sig0) { *u++=((*p&val0)<<shf0) | (p[1]&val1); ++p; }
#define MATCH3(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1, msk2,sig2,val2,shf2) \
if ((*p&msk0)==sig0) { *u++=((*p&val0)<<shf0) | ((p[1]&val1)<<shf1) | (p[2]&val2); p+=2; }
#define MATCH4(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1, msk2,sig2,val2,shf2, msk3,sig3,val3,shf3) \
if ((*p&msk0)==sig0) { *u++=((*p*val0)<<shf0) | ((p[1]&val1)<<shf1) | ((p[2]&val2)<<shf2) | (p[3]&val3); p+=3; }
/* Invoke the appropriate MATCH macro for each case.
*/
#define UTF_CASE(a,z,n,...) \
else MATCH##n(__VA_ARGS__)
/* Allocate buffer.
Iterate through input string,
decode each utf-8 sequence using the table
Return pointer to buffer.
*/
int_least32_t *ucs4_utf8(char *str, int n){
char *p=str;
int_least32_t *u,*buf=u=malloc(n*sizeof*u);
if (buf) {
for (;*p;p++) {
if (0); UCS_RANGE(UTF_CASE)
}
}
return buf;
}
/* Macros to output each of the 4 valid forms of utf-8 encoding.
Encode the appropriate signatures and masked+shifted portions of the UCS-4 value,
updating the p pointer.
*/
#define OUTBYTE1(msk,sig,val,shf) \
*p++=sig|(x&val);
#define OUTBYTE2(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1) \
*p++=sig0|((x>>shf0)&val0); \
*p++=sig1|(x&val1);
#define OUTBYTE3(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1, msk2,sig2,val2,shf2) \
*p++=sig0|((x>>shf0)&val0); \
*p++=sig1|((x>>shf1)&val1); \
*p++=sig2|(x&val2);
#define OUTBYTE4(msk0,sig0,val0,shf0, msk1,sig1,val1,shf1, msk2,sig2,val2,shf2, msk3,sig3,val3,shf3) \
*p++=sig0|((x>>shf0)&val0); \
*p++=sig1|((x>>shf1)&val1); \
*p++=sig2|((x>>shf2)&val2); \
*p++=sig3|(x&val3);
/* Invoke the appropriate OUTBYTE macro for each case
*/
#define UCS_CASE(a,z,n,...) \
else if (ar[i]<z) { int_least32_t x=ar[i]; OUTBYTE##n(__VA_ARGS__) }
/* Allocate buffer.
Iterate through input array,
encode each UCS-4 value using the table
Return pointer to buffer.
*/
char *utf8_ucs4(int_least32_t *ar, int n, int *an){
char *p,*buf=p=malloc(n*4+1);
if (buf) {
int i;
for (i=0; i<n; i++) {
if (0); UCS_RANGE(UCS_CASE)
}
if (an)
*an=p-buf;
*p++=0;
}
return buf;
}
And here's the same code processed with `cpp -P` and
`indent -gnu -i4 -br -ce -cdw -nbc -brf -brs -l100 -bbo`.
int_least32_t *ucs4_utf8 (char *str, int n);
char *utf8_ucs4 (int_least32_t * ar, int n, int *an);
int_least32_t *
ucs4_utf8 (char *str, int n) {
char *p = str;
int_least32_t *u, *buf = u = malloc (n * sizeof *u);
if (buf) {
for (; *p; p++) {
if (0);
else if ((*p & 0x80) == 0x00) {
*u++ = *p & 0x7F;
} else if ((*p & 0xE0) == 0xC0) {
*u++ = ((*p & 0x1F) << 6) | (p[1] & 0x3F);
++p;
} else if ((*p & 0xF0) == 0xE0) {
*u++ = ((*p & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F);
p += 2;
} else if ((*p & 0xF8) == 0xF0) {
*u++ =
((*p *
0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F);
p += 3;
}
}
}
return buf;
}
char *
utf8_ucs4 (int_least32_t * ar, int n, int *an) {
char *p, *buf = p = malloc (n * 4 + 1);
if (buf) {
int i;
for (i = 0; i < n; i++) {
if (0);
else if (ar[i] < 0x0000007F) {
int_least32_t x = ar[i];
*p++ = 0x00 | (x & 0x7F);
} else if (ar[i] < 0x000007FF) {
int_least32_t x = ar[i];
*p++ = 0xC0 | ((x >> 6) & 0x1F);
*p++ = 0x80 | (x & 0x3F);
} else if (ar[i] < 0x0000FFFF) {
int_least32_t x = ar[i];
*p++ = 0xE0 | ((x >> 12) & 0x0F);
*p++ = 0x80 | ((x >> 6) & 0x3F);
*p++ = 0x80 | (x & 0x3F);
} else if (ar[i] < 0x0010FFFF) {
int_least32_t x = ar[i];
*p++ = 0xF0 | ((x >> 18) & 0x07);
*p++ = 0x80 | ((x >> 12) & 0x3F);
*p++ = 0x80 | ((x >> 6) & 0x3F);
*p++ = 0x80 | (x & 0x3F);
}
}
if (an)
*an = p - buf;
*p++ = 0;
}
return buf;
}
[toc] | [next] | [standalone]
| From | Ike Naar <ike@iceland.freeshell.org> |
|---|---|
| Date | 2015-09-20 20:12 +0000 |
| Message-ID | <slrn3vfsmvu4tr.m0.ike@iceland.freeshell.org> |
| In reply to | #70951 |
On 2015-09-19, luser droog <luser.droog@gmail.com> wrote:
> int_least32_t *ucs4_utf8(char *str, int n){
> char *p=str;
> int_least32_t *u,*buf=u=malloc(n*sizeof*u);
Just curious: what is the purpose of 'u'?
> if (buf) {
>
> for (;*p;p++) {
> if (0); UCS_RANGE(UTF_CASE)
Just curious: what is the purpose of 'if (0);' ?
> }
> }
> return buf;
> }
[toc] | [prev] | [next] | [standalone]
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2015-09-20 15:11 -0700 |
| Message-ID | <915a96e2-dfe1-4479-8d78-f5f2a158d4c5@googlegroups.com> |
| In reply to | #70988 |
On Sunday, September 20, 2015 at 3:15:00 PM UTC-5, Ike Naar wrote:
> On 2015-09-19, luser droog <luser.droog@gmail.com> wrote:
> > int_least32_t *ucs4_utf8(char *str, int n){
> > char *p=str;
> > int_least32_t *u,*buf=u=malloc(n*sizeof*u);
>
> Just curious: what is the purpose of 'u'?
'u' is the iterator that runs through the buffer.
> > if (buf) {
> >
> > for (;*p;p++) {
> > if (0); UCS_RANGE(UTF_CASE)
>
> Just curious: what is the purpose of 'if (0);' ?
That was to normalize the form of each clause. After
'if (0);' can follow any number of 'else if (cond) expr;'
portions.
> > }
> > }
> > return buf;
> > }
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-09-20 23:45 +0100 |
| Message-ID | <87oagwaewb.fsf@bsb.me.uk> |
| In reply to | #70988 |
Ike Naar <ike@iceland.freeshell.org> writes:
> On 2015-09-19, luser droog <luser.droog@gmail.com> wrote:
>> int_least32_t *ucs4_utf8(char *str, int n){
>> char *p=str;
>> int_least32_t *u,*buf=u=malloc(n*sizeof*u);
>
> Just curious: what is the purpose of 'u'?
Always assuming these are not rhetorical questions... it's the pointer
that's used to move through the array. You can't tell at all easily
because the code is not designed to be understood.
>> if (buf) {
>>
>> for (;*p;p++) {
>> if (0); UCS_RANGE(UTF_CASE)
>
> Just curious: what is the purpose of 'if (0);' ?
It lets all all of the other cases be the same. By pre-pending a dummy
case, all of the others can have the form "else if (C) S".
I don't really understand the rules of the game here so it would be
churlish to say it's ghastly code. It's probably very good code by
whatever criterion is in play here. I just don't know what that
criterion is.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2015-09-24 18:17 -0700 |
| Message-ID | <9295b0ea-7385-4be6-9fbd-89420552788f@googlegroups.com> |
| In reply to | #71003 |
The goal of the game is to generalize the encoder/decoder code into (an) algorithm template(s) and a table defining the associated ranges in the two representations. Then you can instantiate a family of codices obeying the same form. It's starting to look like the c preprocessor is not the best tool for this task.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web