Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #70951 > unrolled thread

Data-driven template-generation and fancy macros

Started byluser droog <luser.droog@gmail.com>
First post2015-09-19 15:46 -0700
Last post2015-09-24 18:17 -0700
Articles 5 — 3 participants

Back to article view | Back to comp.lang.c


Contents

  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

#70951 — Data-driven template-generation and fancy macros

Fromluser droog <luser.droog@gmail.com>
Date2015-09-19 15:46 -0700
SubjectData-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]


#70988

FromIke Naar <ike@iceland.freeshell.org>
Date2015-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]


#70994

Fromluser droog <luser.droog@gmail.com>
Date2015-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]


#71003

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2015-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]


#71185

Fromluser droog <luser.droog@gmail.com>
Date2015-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