utilities.c 12.3 KB
Newer Older
Teodor Sigaev's avatar
Teodor Sigaev committed
1 2 3 4 5 6 7 8 9 10 11

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "header.h"

#define unless(C) if(!(C))

#define CREATE_SIZE 1

Teodor Sigaev's avatar
Teodor Sigaev committed
12 13 14 15 16 17 18 19 20
extern symbol * create_s(void) {
    symbol * p;
    void * mem = malloc(HEAD + (CREATE_SIZE + 1) * sizeof(symbol));
    if (mem == NULL) return NULL;
    p = (symbol *) (HEAD + (char *) mem);
    CAPACITY(p) = CREATE_SIZE;
    SET_SIZE(p, CREATE_SIZE);
    return p;
}
Bruce Momjian's avatar
Bruce Momjian committed
21

Teodor Sigaev's avatar
Teodor Sigaev committed
22 23 24
extern void lose_s(symbol * p) {
    if (p == NULL) return;
    free((char *) p - HEAD);
Teodor Sigaev's avatar
Teodor Sigaev committed
25 26
}

Teodor Sigaev's avatar
Teodor Sigaev committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
   new_p = X_skip_utf8(p, c, lb, l, n); skips n characters forwards from p + c
   if n +ve, or n characters backwards from p +c - 1 if n -ve. new_p is the new
   position, or 0 on failure.

   -- used to implement hop and next in the utf8 case.
*/

extern int skip_utf8(const symbol * p, int c, int lb, int l, int n) {
    int b;
    if (n >= 0) {
        for (; n > 0; n--) {
            if (c >= l) return -1;
            b = p[c++];
            if (b >= 0xC0) {   /* 1100 0000 */
                while (c < l) {
                    b = p[c];
                    if (b >= 0xC0 || b < 0x80) break;
                    /* break unless b is 10------ */
                    c++;
                }
            }
        }
    } else {
        for (; n < 0; n++) {
            if (c <= lb) return -1;
            b = p[--c];
            if (b >= 0x80) {   /* 1000 0000 */
                while (c > lb) {
                    b = p[c];
                    if (b >= 0xC0) break; /* 1100 0000 */
                    c--;
                }
            }
        }
    }
    return c;
Bruce Momjian's avatar
Bruce Momjian committed
64
}
Teodor Sigaev's avatar
Teodor Sigaev committed
65

Teodor Sigaev's avatar
Teodor Sigaev committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79
/* Code for character groupings: utf8 cases */

static int get_utf8(const symbol * p, int c, int l, int * slot) {
    int b0, b1;
    if (c >= l) return 0;
    b0 = p[c++];
    if (b0 < 0xC0 || c == l) {   /* 1100 0000 */
        * slot = b0; return 1;
    }
    b1 = p[c++];
    if (b0 < 0xE0 || c == l) {   /* 1110 0000 */
        * slot = (b0 & 0x1F) << 6 | (b1 & 0x3F); return 2;
    }
    * slot = (b0 & 0xF) << 12 | (b1 & 0x3F) << 6 | (*p & 0x3F); return 3;
Teodor Sigaev's avatar
Teodor Sigaev committed
80 81
}

Teodor Sigaev's avatar
Teodor Sigaev committed
82 83 84 85 86 87 88 89 90 91 92 93
static int get_b_utf8(const symbol * p, int c, int lb, int * slot) {
    int b0, b1;
    if (c <= lb) return 0;
    b0 = p[--c];
    if (b0 < 0x80 || c == lb) {   /* 1000 0000 */
        * slot = b0; return 1;
    }
    b1 = p[--c];
    if (b1 >= 0xC0 || c == lb) {   /* 1100 0000 */
        * slot = (b1 & 0x1F) << 6 | (b0 & 0x3F); return 2;
    }
    * slot = (*p & 0xF) << 12 | (b1 & 0x3F) << 6 | (b0 & 0x3F); return 3;
Teodor Sigaev's avatar
Teodor Sigaev committed
94 95
}

Teodor Sigaev's avatar
Teodor Sigaev committed
96 97 98 99 100 101
extern int in_grouping_U(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    int w = get_utf8(z->p, z->c, z->l, & ch);
    unless (w) return 0;
    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c += w; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
102 103
}

Teodor Sigaev's avatar
Teodor Sigaev committed
104 105 106 107 108 109
extern int in_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
    unless (w) return 0;
    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c -= w; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
110 111
}

Teodor Sigaev's avatar
Teodor Sigaev committed
112 113 114 115 116 117 118
extern int out_grouping_U(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    int w = get_utf8(z->p, z->c, z->l, & ch);
    unless (w) return 0;
    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c += w; return 1;
}
Teodor Sigaev's avatar
Teodor Sigaev committed
119

Teodor Sigaev's avatar
Teodor Sigaev committed
120 121 122 123 124 125
extern int out_grouping_b_U(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
    unless (w) return 0;
    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c -= w; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
126 127
}

Teodor Sigaev's avatar
Teodor Sigaev committed
128 129 130 131 132 133 134 135
/* Code for character groupings: non-utf8 cases */

extern int in_grouping(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    if (z->c >= z->l) return 0;
    ch = z->p[z->c];
    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c++; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
136 137
}

Teodor Sigaev's avatar
Teodor Sigaev committed
138 139 140 141 142 143
extern int in_grouping_b(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    if (z->c <= z->lb) return 0;
    ch = z->p[z->c - 1];
    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c--; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
144 145
}

Teodor Sigaev's avatar
Teodor Sigaev committed
146 147 148 149 150 151
extern int out_grouping(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    if (z->c >= z->l) return 0;
    ch = z->p[z->c];
    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c++; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
152 153
}

Teodor Sigaev's avatar
Teodor Sigaev committed
154 155 156 157 158 159
extern int out_grouping_b(struct SN_env * z, unsigned char * s, int min, int max) {
    int ch;
    if (z->c <= z->lb) return 0;
    ch = z->p[z->c - 1];
    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
    z->c--; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
160 161
}

Teodor Sigaev's avatar
Teodor Sigaev committed
162 163 164
extern int eq_s(struct SN_env * z, int s_size, symbol * s) {
    if (z->l - z->c < s_size || memcmp(z->p + z->c, s, s_size * sizeof(symbol)) != 0) return 0;
    z->c += s_size; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
165 166
}

Teodor Sigaev's avatar
Teodor Sigaev committed
167 168 169
extern int eq_s_b(struct SN_env * z, int s_size, symbol * s) {
    if (z->c - z->lb < s_size || memcmp(z->p + z->c - s_size, s, s_size * sizeof(symbol)) != 0) return 0;
    z->c -= s_size; return 1;
Teodor Sigaev's avatar
Teodor Sigaev committed
170 171
}

Teodor Sigaev's avatar
Teodor Sigaev committed
172 173
extern int eq_v(struct SN_env * z, symbol * p) {
    return eq_s(z, SIZE(p), p);
Teodor Sigaev's avatar
Teodor Sigaev committed
174 175
}

Teodor Sigaev's avatar
Teodor Sigaev committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
extern int eq_v_b(struct SN_env * z, symbol * p) {
    return eq_s_b(z, SIZE(p), p);
}

extern int find_among(struct SN_env * z, struct among * v, int v_size) {

    int i = 0;
    int j = v_size;

    int c = z->c; int l = z->l;
    symbol * q = z->p + c;

    struct among * w;

    int common_i = 0;
    int common_j = 0;

    int first_key_inspected = 0;

    while(1) {
        int k = i + ((j - i) >> 1);
        int diff = 0;
        int common = common_i < common_j ? common_i : common_j; /* smaller */
        w = v + k;
        {
            int i; for (i = common; i < w->s_size; i++) {
                if (c + common == l) { diff = -1; break; }
                diff = q[common] - w->s[i];
                if (diff != 0) break;
                common++;
            }
        }
        if (diff < 0) { j = k; common_j = common; }
                 else { i = k; common_i = common; }
        if (j - i <= 1) {
            if (i > 0) break; /* v->s has been inspected */
            if (j == i) break; /* only one item in v */

            /* - but now we need to go round once more to get
               v->s inspected. This looks messy, but is actually
               the optimal approach.  */

            if (first_key_inspected) break;
            first_key_inspected = 1;
        }
    }
    while(1) {
        w = v + i;
        if (common_i >= w->s_size) {
            z->c = c + w->s_size;
            if (w->function == 0) return w->result;
            {
                int res = w->function(z);
                z->c = c + w->s_size;
                if (res) return w->result;
            }
        }
        i = w->substring_i;
        if (i < 0) return 0;
    }
Teodor Sigaev's avatar
Teodor Sigaev committed
236 237 238 239
}

/* find_among_b is for backwards processing. Same comments apply */

Teodor Sigaev's avatar
Teodor Sigaev committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
extern int find_among_b(struct SN_env * z, struct among * v, int v_size) {

    int i = 0;
    int j = v_size;

    int c = z->c; int lb = z->lb;
    symbol * q = z->p + c - 1;

    struct among * w;

    int common_i = 0;
    int common_j = 0;

    int first_key_inspected = 0;

    while(1) {
        int k = i + ((j - i) >> 1);
        int diff = 0;
        int common = common_i < common_j ? common_i : common_j;
        w = v + k;
        {
            int i; for (i = w->s_size - 1 - common; i >= 0; i--) {
                if (c - common == lb) { diff = -1; break; }
                diff = q[- common] - w->s[i];
                if (diff != 0) break;
                common++;
            }
        }
        if (diff < 0) { j = k; common_j = common; }
                 else { i = k; common_i = common; }
        if (j - i <= 1) {
            if (i > 0) break;
            if (j == i) break;
            if (first_key_inspected) break;
            first_key_inspected = 1;
        }
    }
    while(1) {
        w = v + i;
        if (common_i >= w->s_size) {
            z->c = c - w->s_size;
            if (w->function == 0) return w->result;
            {
                int res = w->function(z);
                z->c = c - w->s_size;
                if (res) return w->result;
            }
        }
        i = w->substring_i;
        if (i < 0) return 0;
    }
Teodor Sigaev's avatar
Teodor Sigaev committed
291 292 293
}


Teodor Sigaev's avatar
Teodor Sigaev committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
/* Increase the size of the buffer pointed to by p to at least n symbols.
 * If insufficient memory, returns NULL and frees the old buffer.
 */
static symbol * increase_size(symbol * p, int n) {
    symbol * q;
    int new_size = n + 20;
    void * mem = realloc((char *) p - HEAD,
                         HEAD + (new_size + 1) * sizeof(symbol));
    if (mem == NULL) {
        lose_s(p);
        return NULL;
    }
    q = (symbol *) (HEAD + (char *)mem);
    CAPACITY(q) = new_size;
    return q;
Teodor Sigaev's avatar
Teodor Sigaev committed
309 310 311
}

/* to replace symbols between c_bra and c_ket in z->p by the
Teodor Sigaev's avatar
Teodor Sigaev committed
312 313 314
   s_size symbols at s.
   Returns 0 on success, -1 on error.
   Also, frees z->p (and sets it to NULL) on error.
Teodor Sigaev's avatar
Teodor Sigaev committed
315
*/
Teodor Sigaev's avatar
Teodor Sigaev committed
316
extern int replace_s(struct SN_env * z, int c_bra, int c_ket, int s_size, const symbol * s, int * adjptr)
Bruce Momjian's avatar
Bruce Momjian committed
317
{
Teodor Sigaev's avatar
Teodor Sigaev committed
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    int adjustment;
    int len;
    if (z->p == NULL) {
        z->p = create_s();
        if (z->p == NULL) return -1;
    }
    adjustment = s_size - (c_ket - c_bra);
    len = SIZE(z->p);
    if (adjustment != 0) {
        if (adjustment + len > CAPACITY(z->p)) {
            z->p = increase_size(z->p, adjustment + len);
            if (z->p == NULL) return -1;
        }
        memmove(z->p + c_ket + adjustment,
                z->p + c_ket,
                (len - c_ket) * sizeof(symbol));
        SET_SIZE(z->p, adjustment + len);
        z->l += adjustment;
        if (z->c >= c_ket)
            z->c += adjustment;
        else
            if (z->c > c_bra)
                z->c = c_bra;
    }
    unless (s_size == 0) memmove(z->p + c_bra, s, s_size * sizeof(symbol));
    if (adjptr != NULL)
        *adjptr = adjustment;
    return 0;
Teodor Sigaev's avatar
Teodor Sigaev committed
346 347
}

Teodor Sigaev's avatar
Teodor Sigaev committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
static int slice_check(struct SN_env * z) {

    if (z->bra < 0 ||
        z->bra > z->ket ||
        z->ket > z->l ||
        z->p == NULL ||
        z->l > SIZE(z->p)) /* this line could be removed */
    {
#if 0
        fprintf(stderr, "faulty slice operation:\n");
        debug(z, -1, 0);
#endif
        return -1;
    }
    return 0;
Teodor Sigaev's avatar
Teodor Sigaev committed
363 364
}

Teodor Sigaev's avatar
Teodor Sigaev committed
365 366 367
extern int slice_from_s(struct SN_env * z, int s_size, symbol * s) {
    if (slice_check(z)) return -1;
    return replace_s(z, z->bra, z->ket, s_size, s, NULL);
Teodor Sigaev's avatar
Teodor Sigaev committed
368 369
}

Teodor Sigaev's avatar
Teodor Sigaev committed
370 371
extern int slice_from_v(struct SN_env * z, symbol * p) {
    return slice_from_s(z, SIZE(p), p);
Teodor Sigaev's avatar
Teodor Sigaev committed
372 373
}

Teodor Sigaev's avatar
Teodor Sigaev committed
374 375
extern int slice_del(struct SN_env * z) {
    return slice_from_s(z, 0, 0);
Teodor Sigaev's avatar
Teodor Sigaev committed
376 377
}

Teodor Sigaev's avatar
Teodor Sigaev committed
378 379 380 381 382 383 384
extern int insert_s(struct SN_env * z, int bra, int ket, int s_size, symbol * s) {
    int adjustment;
    if (replace_s(z, bra, ket, s_size, s, &adjustment))
        return -1;
    if (bra <= z->bra) z->bra += adjustment;
    if (bra <= z->ket) z->ket += adjustment;
    return 0;
Teodor Sigaev's avatar
Teodor Sigaev committed
385 386
}

Teodor Sigaev's avatar
Teodor Sigaev committed
387 388 389 390 391 392 393
extern int insert_v(struct SN_env * z, int bra, int ket, symbol * p) {
    int adjustment;
    if (replace_s(z, bra, ket, SIZE(p), p, &adjustment))
        return -1;
    if (bra <= z->bra) z->bra += adjustment;
    if (bra <= z->ket) z->ket += adjustment;
    return 0;
Teodor Sigaev's avatar
Teodor Sigaev committed
394 395
}

Teodor Sigaev's avatar
Teodor Sigaev committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
extern symbol * slice_to(struct SN_env * z, symbol * p) {
    if (slice_check(z)) {
        lose_s(p);
        return NULL;
    }
    {
        int len = z->ket - z->bra;
        if (CAPACITY(p) < len) {
            p = increase_size(p, len);
            if (p == NULL)
                return NULL;
        }
        memmove(p, z->p + z->bra, len * sizeof(symbol));
        SET_SIZE(p, len);
    }
    return p;
Teodor Sigaev's avatar
Teodor Sigaev committed
412 413
}

Teodor Sigaev's avatar
Teodor Sigaev committed
414 415 416 417 418 419 420 421 422 423
extern symbol * assign_to(struct SN_env * z, symbol * p) {
    int len = z->l;
    if (CAPACITY(p) < len) {
        p = increase_size(p, len);
        if (p == NULL)
            return NULL;
    }
    memmove(p, z->p, len * sizeof(symbol));
    SET_SIZE(p, len);
    return p;
Teodor Sigaev's avatar
Teodor Sigaev committed
424 425
}

Teodor Sigaev's avatar
Teodor Sigaev committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
#if 0
extern void debug(struct SN_env * z, int number, int line_count) {
    int i;
    int limit = SIZE(z->p);
    /*if (number >= 0) printf("%3d (line %4d): '", number, line_count);*/
    if (number >= 0) printf("%3d (line %4d): [%d]'", number, line_count,limit);
    for (i = 0; i <= limit; i++) {
        if (z->lb == i) printf("{");
        if (z->bra == i) printf("[");
        if (z->c == i) printf("|");
        if (z->ket == i) printf("]");
        if (z->l == i) printf("}");
        if (i < limit)
        {   int ch = z->p[i];
            if (ch == 0) ch = '#';
            printf("%c", ch);
        }
    }
    printf("'\n");
Teodor Sigaev's avatar
Teodor Sigaev committed
445
}
Teodor Sigaev's avatar
Teodor Sigaev committed
446
#endif