summaryrefslogtreecommitdiff
path: root/appl/lib/rabin.b
blob: 6a33011a0448127a314c2ca484acde67cc575a6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
implement Rabin;

include "sys.m";
	sys: Sys;
include "bufio.m";
	bufio: Bufio;
	Iobuf, EOF, ERROR: import bufio;
include "rabin.m";

sprint: import sys;

init(b: Bufio)
{
	sys = load Sys Sys->PATH;
	bufio = b;
}

modpower(base, n, mod: int): int
{
	power := 1;
	for(i := 0; i < n; i++)
		power = (power * base) % mod;
	return power;
}

Rcfg.mk(prime, width, mod: int): (ref Rcfg, string)
{
	rcfg := ref Rcfg(prime, width, mod, array[256] of int);
	power := modpower(prime, width, mod);
	for(i := 0; i < 256; i++)
		rcfg.tab[i] = (i * power) % mod;
	return (rcfg, nil);
}


open(rcfg: ref Rcfg, b: ref Iobuf, min, max: int): (ref Rfile, string)
{
	if(min > max)
		return (nil, sprint("bad min/max"));
	if(min < rcfg.width)
		return (nil, "min < width");
	r := ref Rfile(b, rcfg, min, max, array[max+rcfg.width] of byte, 0, 0, big 0);

	(prime, width, mod) := (r.rcfg.prime, r.rcfg.width, r.rcfg.mod);
	while(r.n < width) {
		ch := r.b.getb();
		if(ch == ERROR)
			return (nil, sprint("reading: %r"));
		if(ch == EOF)
			break;
		r.buf[r.n] = byte ch;
		r.state = (prime*r.state + ch) % mod;
		r.n++;
	}
	return (r, nil);
}

Rfile.read(r: self ref Rfile): (array of byte, big, string)
{
	(prime, width, mod) := (r.rcfg.prime, r.rcfg.width, r.rcfg.mod);
	for(;;) {
		ch := r.b.getb();
		if(ch == ERROR)
			return (nil, big 0, sprint("reading: %r"));
		if(ch == EOF) {
			d := r.buf[:r.n];
			off := r.off;
			r.n = 0;
			r.off += big len d;
			return (d, off, nil);
		}
		r.buf[r.n] = byte ch;
		r.state = (mod+prime*r.state + ch - r.rcfg.tab[int r.buf[r.n-width]]) % mod;
		r.n++;
		if(r.n-width >= r.max || (r.n-width >= r.min && r.state == mod-1)) {
			d := array[r.n-width] of byte;
			d[:] = r.buf[:len d];
			off := r.off;
			r.buf[:] = r.buf[r.n-width:r.n];
			r.n = width;
			r.off += big len d;
			return (d, off, nil);
		}
	}
}