OSDN Git Service

libjava:
[pf3gnuchains/gcc-fork.git] / boehm-gc / cord / cordxtra.c
1 /*
2  * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
3  *
4  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
5  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
6  *
7  * Permission is hereby granted to use or copy this program
8  * for any purpose,  provided the above notices are retained on all copies.
9  * Permission to modify the code and to distribute modified code is granted,
10  * provided the above notices are retained, and a notice that the code was
11  * modified is included with the above copyright notice.
12  *
13  * Author: Hans-J. Boehm (boehm@parc.xerox.com)
14  */
15 /*
16  * These are functions on cords that do not need to understand their
17  * implementation.  They serve also serve as example client code for
18  * cord_basics.
19  */
20 /* Boehm, December 8, 1995 1:53 pm PST */
21 # include <stdio.h>
22 # include <string.h>
23 # include <stdlib.h>
24 # include <stdarg.h>
25 # include "cord.h"
26 # include "ec.h"
27 # define I_HIDE_POINTERS        /* So we get access to allocation lock. */
28                                 /* We use this for lazy file reading,   */
29                                 /* so that we remain independent        */
30                                 /* of the threads primitives.           */
31 # include "gc.h"
32
33 /* For now we assume that pointer reads and writes are atomic,  */
34 /* i.e. another thread always sees the state before or after    */
35 /* a write.  This might be false on a Motorola M68K with        */
36 /* pointers that are not 32-bit aligned.  But there probably    */
37 /* aren't too many threads packages running on those.           */
38 # define ATOMIC_WRITE(x,y) (x) = (y)
39 # define ATOMIC_READ(x) (*(x))
40
41 /* The standard says these are in stdio.h, but they aren't always: */
42 # ifndef SEEK_SET
43 #   define SEEK_SET 0
44 # endif
45 # ifndef SEEK_END
46 #   define SEEK_END 2
47 # endif
48
49 # define BUFSZ 2048     /* Size of stack allocated buffers when         */
50                         /* we want large buffers.                       */
51
52 typedef void (* oom_fn)(void);
53
54 # define OUT_OF_MEMORY {  if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \
55                           ABORT("Out of memory\n"); }
56 # define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); }
57
58 CORD CORD_cat_char(CORD x, char c)
59 {
60     register char * string;
61     
62     if (c == '\0') return(CORD_cat(x, CORD_nul(1)));
63     string = GC_MALLOC_ATOMIC(2);
64     if (string == 0) OUT_OF_MEMORY;
65     string[0] = c;
66     string[1] = '\0';
67     return(CORD_cat_char_star(x, string, 1));
68 }
69
70 CORD CORD_catn(int nargs, ...)
71 {
72     register CORD result = CORD_EMPTY;
73     va_list args;
74     register int i;
75
76     va_start(args, nargs);
77     for (i = 0; i < nargs; i++) {
78         register CORD next = va_arg(args, CORD);
79         result = CORD_cat(result, next);
80     }
81     va_end(args);
82     return(result);
83 }
84
85 typedef struct {
86         size_t len;
87         size_t count;
88         char * buf;
89 } CORD_fill_data;
90
91 int CORD_fill_proc(char c, void * client_data)
92 {
93     register CORD_fill_data * d = (CORD_fill_data *)client_data;
94     register size_t count = d -> count;
95     
96     (d -> buf)[count] = c;
97     d -> count = ++count;
98     if (count >= d -> len) {
99         return(1);
100     } else {
101         return(0);
102     }
103 }
104
105 int CORD_batched_fill_proc(const char * s, void * client_data)
106 {
107     register CORD_fill_data * d = (CORD_fill_data *)client_data;
108     register size_t count = d -> count;
109     register size_t max = d -> len;
110     register char * buf = d -> buf;
111     register const char * t = s;
112     
113     while((buf[count] = *t++) != '\0') {
114         count++;
115         if (count >= max) {
116             d -> count = count;
117             return(1);
118         }
119     }
120     d -> count = count;
121     return(0);
122 }
123
124 /* Fill buf with len characters starting at i.                          */
125 /* Assumes len characters are available.                                */ 
126 void CORD_fill_buf(CORD x, size_t i, size_t len, char * buf)
127 {
128     CORD_fill_data fd;
129     
130     fd.len = len;
131     fd.buf = buf;
132     fd.count = 0;
133     (void)CORD_iter5(x, i, CORD_fill_proc, CORD_batched_fill_proc, &fd);
134 }
135
136 int CORD_cmp(CORD x, CORD y)
137 {
138     CORD_pos xpos;
139     CORD_pos ypos;
140     register size_t avail, yavail;
141     
142     if (y == CORD_EMPTY) return(x != CORD_EMPTY);
143     if (x == CORD_EMPTY) return(-1);
144     if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y));
145     CORD_set_pos(xpos, x, 0);
146     CORD_set_pos(ypos, y, 0);
147     for(;;) {
148         if (!CORD_pos_valid(xpos)) {
149             if (CORD_pos_valid(ypos)) {
150                 return(-1);
151             } else {
152                 return(0);
153             }
154         }
155         if (!CORD_pos_valid(ypos)) {
156             return(1);
157         }
158         if ((avail = CORD_pos_chars_left(xpos)) <= 0
159             || (yavail = CORD_pos_chars_left(ypos)) <= 0) {
160             register char xcurrent = CORD_pos_fetch(xpos);
161             register char ycurrent = CORD_pos_fetch(ypos);
162             if (xcurrent != ycurrent) return(xcurrent - ycurrent);
163             CORD_next(xpos);
164             CORD_next(ypos);
165         } else {
166             /* process as many characters as we can     */
167             register int result;
168             
169             if (avail > yavail) avail = yavail;
170             result = strncmp(CORD_pos_cur_char_addr(xpos),
171                              CORD_pos_cur_char_addr(ypos), avail);
172             if (result != 0) return(result);
173             CORD_pos_advance(xpos, avail);
174             CORD_pos_advance(ypos, avail);
175         }
176     }
177 }
178
179 int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len)
180 {
181     CORD_pos xpos;
182     CORD_pos ypos;
183     register size_t count;
184     register long avail, yavail;
185     
186     CORD_set_pos(xpos, x, x_start);
187     CORD_set_pos(ypos, y, y_start);
188     for(count = 0; count < len;) {
189         if (!CORD_pos_valid(xpos)) {
190             if (CORD_pos_valid(ypos)) {
191                 return(-1);
192             } else {
193                 return(0);
194             }
195         }
196         if (!CORD_pos_valid(ypos)) {
197             return(1);
198         }
199         if ((avail = CORD_pos_chars_left(xpos)) <= 0
200             || (yavail = CORD_pos_chars_left(ypos)) <= 0) {
201             register char xcurrent = CORD_pos_fetch(xpos);
202             register char ycurrent = CORD_pos_fetch(ypos);
203             if (xcurrent != ycurrent) return(xcurrent - ycurrent);
204             CORD_next(xpos);
205             CORD_next(ypos);
206             count++;
207         } else {
208             /* process as many characters as we can     */
209             register int result;
210             
211             if (avail > yavail) avail = yavail;
212             count += avail;
213             if (count > len) avail -= (count - len);
214             result = strncmp(CORD_pos_cur_char_addr(xpos),
215                              CORD_pos_cur_char_addr(ypos), (size_t)avail);
216             if (result != 0) return(result);
217             CORD_pos_advance(xpos, (size_t)avail);
218             CORD_pos_advance(ypos, (size_t)avail);
219         }
220     }
221     return(0);
222 }
223
224 char * CORD_to_char_star(CORD x)
225 {
226     register size_t len = CORD_len(x);
227     char * result = GC_MALLOC_ATOMIC(len + 1);
228     
229     if (result == 0) OUT_OF_MEMORY;
230     CORD_fill_buf(x, 0, len, result);
231     result[len] = '\0';
232     return(result);
233 }
234
235 CORD CORD_from_char_star(const char *s)
236 {
237     char * result;
238     size_t len = strlen(s);
239
240     if (0 == len) return(CORD_EMPTY);
241     result = GC_MALLOC_ATOMIC(len + 1);
242     if (result == 0) OUT_OF_MEMORY;
243     memcpy(result, s, len+1);
244     return(result);
245 }
246
247 const char * CORD_to_const_char_star(CORD x)
248 {
249     if (x == 0) return("");
250     if (CORD_IS_STRING(x)) return((const char *)x);
251     return(CORD_to_char_star(x));
252 }
253
254 char CORD_fetch(CORD x, size_t i)
255 {
256     CORD_pos xpos;
257     
258     CORD_set_pos(xpos, x, i);
259     if (!CORD_pos_valid(xpos)) ABORT("bad index?");
260     return(CORD_pos_fetch(xpos));
261 }
262
263
264 int CORD_put_proc(char c, void * client_data)
265 {
266     register FILE * f = (FILE *)client_data;
267     
268     return(putc(c, f) == EOF);
269 }
270
271 int CORD_batched_put_proc(const char * s, void * client_data)
272 {
273     register FILE * f = (FILE *)client_data;
274     
275     return(fputs(s, f) == EOF);
276 }
277     
278
279 int CORD_put(CORD x, FILE * f)
280 {
281     if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) {
282         return(EOF);
283     } else {
284         return(1);
285     }
286 }
287
288 typedef struct {
289     size_t pos;         /* Current position in the cord */
290     char target;        /* Character we're looking for  */
291 } chr_data;
292
293 int CORD_chr_proc(char c, void * client_data)
294 {
295     register chr_data * d = (chr_data *)client_data;
296     
297     if (c == d -> target) return(1);
298     (d -> pos) ++;
299     return(0);
300 }
301
302 int CORD_rchr_proc(char c, void * client_data)
303 {
304     register chr_data * d = (chr_data *)client_data;
305     
306     if (c == d -> target) return(1);
307     (d -> pos) --;
308     return(0);
309 }
310
311 int CORD_batched_chr_proc(const char *s, void * client_data)
312 {
313     register chr_data * d = (chr_data *)client_data;
314     register char * occ = strchr(s, d -> target);
315     
316     if (occ == 0) {
317         d -> pos += strlen(s);
318         return(0);
319     } else {
320         d -> pos += occ - s;
321         return(1);
322     }
323 }
324
325 size_t CORD_chr(CORD x, size_t i, int c)
326 {
327     chr_data d;
328     
329     d.pos = i;
330     d.target = c;
331     if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) {
332         return(d.pos);
333     } else {
334         return(CORD_NOT_FOUND);
335     }
336 }
337
338 size_t CORD_rchr(CORD x, size_t i, int c)
339 {
340     chr_data d;
341     
342     d.pos = i;
343     d.target = c;
344     if (CORD_riter4(x, i, CORD_rchr_proc, &d)) {
345         return(d.pos);
346     } else {
347         return(CORD_NOT_FOUND);
348     }
349 }
350
351 /* Find the first occurrence of s in x at position start or later.      */
352 /* This uses an asymptotically poor algorithm, which should typically   */
353 /* perform acceptably.  We compare the first few characters directly,   */
354 /* and call CORD_ncmp whenever there is a partial match.                */
355 /* This has the advantage that we allocate very little, or not at all.  */
356 /* It's very fast if there are few close misses.                        */
357 size_t CORD_str(CORD x, size_t start, CORD s)
358 {
359     CORD_pos xpos;
360     size_t xlen = CORD_len(x);
361     size_t slen;
362     register size_t start_len;
363     const char * s_start;
364     unsigned long s_buf = 0;    /* The first few characters of s        */
365     unsigned long x_buf = 0;    /* Start of candidate substring.        */
366                                 /* Initialized only to make compilers   */
367                                 /* happy.                               */
368     unsigned long mask = 0;
369     register size_t i;
370     register size_t match_pos;
371     
372     if (s == CORD_EMPTY) return(start);
373     if (CORD_IS_STRING(s)) {
374         s_start = s;
375         slen = strlen(s);
376     } else {
377         s_start = CORD_to_char_star(CORD_substr(s, 0, sizeof(unsigned long)));
378         slen = CORD_len(s);
379     }
380     if (xlen < start || xlen - start < slen) return(CORD_NOT_FOUND);
381     start_len = slen;
382     if (start_len > sizeof(unsigned long)) start_len = sizeof(unsigned long);
383     CORD_set_pos(xpos, x, start);
384     for (i = 0; i < start_len; i++) {
385         mask <<= 8;
386         mask |= 0xff;
387         s_buf <<= 8;
388         s_buf |= (unsigned char)s_start[i];
389         x_buf <<= 8;
390         x_buf |= (unsigned char)CORD_pos_fetch(xpos);
391         CORD_next(xpos);
392     }
393     for (match_pos = start; ; match_pos++) {
394         if ((x_buf & mask) == s_buf) {
395             if (slen == start_len ||
396                 CORD_ncmp(x, match_pos + start_len,
397                           s, start_len, slen - start_len) == 0) {
398                 return(match_pos);
399             }
400         }
401         if ( match_pos == xlen - slen ) {
402             return(CORD_NOT_FOUND);
403         }
404         x_buf <<= 8;
405         x_buf |= (unsigned char)CORD_pos_fetch(xpos);
406         CORD_next(xpos);
407     }
408 }
409
410 void CORD_ec_flush_buf(CORD_ec x)
411 {
412     register size_t len = x[0].ec_bufptr - x[0].ec_buf;
413     char * s;
414
415     if (len == 0) return;
416     s = GC_MALLOC_ATOMIC(len+1);
417     memcpy(s, x[0].ec_buf, len);
418     s[len] = '\0';
419     x[0].ec_cord = CORD_cat_char_star(x[0].ec_cord, s, len);
420     x[0].ec_bufptr = x[0].ec_buf;
421 }
422
423 void CORD_ec_append_cord(CORD_ec x, CORD s)
424 {
425     CORD_ec_flush_buf(x);
426     x[0].ec_cord = CORD_cat(x[0].ec_cord, s);
427 }
428
429 /*ARGSUSED*/
430 char CORD_nul_func(size_t i, void * client_data)
431 {
432     return((char)(unsigned long)client_data);
433 }
434
435
436 CORD CORD_chars(char c, size_t i)
437 {
438     return(CORD_from_fn(CORD_nul_func, (void *)(unsigned long)c, i));
439 }
440
441 CORD CORD_from_file_eager(FILE * f)
442 {
443     register int c;
444     CORD_ec ecord;
445     
446     CORD_ec_init(ecord);
447     for(;;) {
448         c = getc(f);
449         if (c == 0) {
450           /* Append the right number of NULs    */
451           /* Note that any string of NULs is rpresented in 4 words,     */
452           /* independent of its length.                                 */
453             register size_t count = 1;
454             
455             CORD_ec_flush_buf(ecord);
456             while ((c = getc(f)) == 0) count++;
457             ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count));
458         }
459         if (c == EOF) break;
460         CORD_ec_append(ecord, c);
461     }
462     (void) fclose(f);
463     return(CORD_balance(CORD_ec_to_cord(ecord)));
464 }
465
466 /* The state maintained for a lazily read file consists primarily       */
467 /* of a large direct-mapped cache of previously read values.            */
468 /* We could rely more on stdio buffering.  That would have 2            */
469 /* disadvantages:                                                       */
470 /*      1) Empirically, not all fseek implementations preserve the      */
471 /*         buffer whenever they could.                                  */
472 /*      2) It would fail if 2 different sections of a long cord         */
473 /*         were being read alternately.                                 */
474 /* We do use the stdio buffer for read ahead.                           */
475 /* To guarantee thread safety in the presence of atomic pointer         */
476 /* writes, cache lines are always replaced, and never modified in       */
477 /* place.                                                               */
478
479 # define LOG_CACHE_SZ 14
480 # define CACHE_SZ (1 << LOG_CACHE_SZ)
481 # define LOG_LINE_SZ 9
482 # define LINE_SZ (1 << LOG_LINE_SZ)
483
484 typedef struct {
485     size_t tag;
486     char data[LINE_SZ];
487         /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ        */
488 } cache_line;
489
490 typedef struct {
491     FILE * lf_file;
492     size_t lf_current;  /* Current file pointer value */
493     cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ];
494 } lf_state;
495
496 # define MOD_CACHE_SZ(n) ((n) & (CACHE_SZ - 1))
497 # define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ)
498 # define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1))
499 # define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ)
500 # define LINE_START(n) ((n) & ~(LINE_SZ - 1))
501
502 typedef struct {
503     lf_state * state;
504     size_t file_pos;    /* Position of needed character. */
505     cache_line * new_cache;
506 } refill_data;
507
508 /* Executed with allocation lock. */
509 static char refill_cache(client_data)
510 refill_data * client_data;
511 {
512     register lf_state * state = client_data -> state;
513     register size_t file_pos = client_data -> file_pos;
514     FILE *f = state -> lf_file;
515     size_t line_start = LINE_START(file_pos);
516     size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos));
517     cache_line * new_cache = client_data -> new_cache;
518     
519     if (line_start != state -> lf_current
520         && fseek(f, line_start, SEEK_SET) != 0) {
521             ABORT("fseek failed");
522     }
523     if (fread(new_cache -> data, sizeof(char), LINE_SZ, f)
524         <= file_pos - line_start) {
525         ABORT("fread failed");
526     }
527     new_cache -> tag = DIV_LINE_SZ(file_pos);
528     /* Store barrier goes here. */
529     ATOMIC_WRITE(state -> lf_cache[line_no], new_cache);
530     state -> lf_current = line_start + LINE_SZ;
531     return(new_cache->data[MOD_LINE_SZ(file_pos)]);
532 }
533
534 char CORD_lf_func(size_t i, void * client_data)
535 {
536     register lf_state * state = (lf_state *)client_data;
537     register cache_line * volatile * cl_addr =
538                 &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]);
539     register cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr);
540     
541     if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) {
542         /* Cache miss */
543         refill_data rd;
544         
545         rd.state = state;
546         rd.file_pos =  i;
547         rd.new_cache = GC_NEW_ATOMIC(cache_line);
548         if (rd.new_cache == 0) OUT_OF_MEMORY;
549         return((char)(GC_word)
550                   GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd));
551     }
552     return(cl -> data[MOD_LINE_SZ(i)]);
553 }    
554
555 /*ARGSUSED*/
556 void CORD_lf_close_proc(void * obj, void * client_data)  
557 {
558     if (fclose(((lf_state *)obj) -> lf_file) != 0) {
559         ABORT("CORD_lf_close_proc: fclose failed");
560     }
561 }                       
562
563 CORD CORD_from_file_lazy_inner(FILE * f, size_t len)
564 {
565     register lf_state * state = GC_NEW(lf_state);
566     register int i;
567     
568     if (state == 0) OUT_OF_MEMORY;
569     if (len != 0) {
570         /* Dummy read to force buffer allocation.       */
571         /* This greatly increases the probability       */
572         /* of avoiding deadlock if buffer allocation    */
573         /* is redirected to GC_malloc and the           */
574         /* world is multithreaded.                      */
575         char buf[1];
576
577         (void) fread(buf, 1, 1, f); 
578         rewind(f);
579     }
580     state -> lf_file = f;
581     for (i = 0; i < CACHE_SZ/LINE_SZ; i++) {
582         state -> lf_cache[i] = 0;
583     }
584     state -> lf_current = 0;
585     GC_REGISTER_FINALIZER(state, CORD_lf_close_proc, 0, 0, 0);
586     return(CORD_from_fn(CORD_lf_func, state, len));
587 }
588
589 CORD CORD_from_file_lazy(FILE * f)
590 {
591     register long len;
592     
593     if (fseek(f, 0l, SEEK_END) != 0) {
594         ABORT("Bad fd argument - fseek failed");
595     }
596     if ((len = ftell(f)) < 0) {
597         ABORT("Bad fd argument - ftell failed");
598     }
599     rewind(f);
600     return(CORD_from_file_lazy_inner(f, (size_t)len));
601 }
602
603 # define LAZY_THRESHOLD (128*1024 + 1)
604
605 CORD CORD_from_file(FILE * f)
606 {
607     register long len;
608     
609     if (fseek(f, 0l, SEEK_END) != 0) {
610         ABORT("Bad fd argument - fseek failed");
611     }
612     if ((len = ftell(f)) < 0) {
613         ABORT("Bad fd argument - ftell failed");
614     }
615     rewind(f);
616     if (len < LAZY_THRESHOLD) {
617         return(CORD_from_file_eager(f));
618     } else {
619         return(CORD_from_file_lazy_inner(f, (size_t)len));
620     }
621 }