-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathglue.tcl
181 lines (153 loc) · 5.2 KB
/
glue.tcl
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# -*- tcl -*-
## Ankh - Andreas Kupries Hashes
## (c) 2021-2024 Andreas Kupries http://wiki.tcl.tk/andreas%20kupries
# Hash command implementations.
# # ## ### ##### ######## #############
critcl::ccode {
#define CHUNK_SIZE (16*1024*1024) /* 16 MiB */
#define BUF_SIZE (100*1000) /* 0.5 MiB (max: 1 MB) */
}
# Introspect the set of builtin hashes
critcl::cconst ::ak::hash::list char* "\"[lsort -dict [dict keys [hashes]]]\""
# (***) Get all the headers and sources first.
dict for {hash spec} [hashes] {
set h [dict get $spec header]
if {$h eq {}} continue
critcl::cheaders $h
critcl::include [file tail $h]
}
dict for {hash spec} [hashes] {
set c [dict get $spec csource]
if {$c eq {}} continue
critcl::csources $c
}
dict for {hash spec} [hashes] {
dict with spec {}
# - refs References to articles, implementations, ...
# - hsize Size of hash digest, numeric, or symbolic
# - csource Name of C source file to compile (IGNORED HERE, see (***))
# - header Name of header file to include (IGNORED HERE, see (***))
# - context Name of C type for the internal hash state
# - init Name of C function to initialize the hash state
# - update Name of C function to update the hash state with a message chunk
# - final Name of C function to fialize the hash state ito a digest
# - forder Code for argument order of final.
lappend map @@ [string map {/ _} $hash]
lappend map @init@ $init
lappend map @update@ $update
lappend map @final@ $final
lappend map @context@ $context
lappend map @hsize@ $hsize
lappend map @fargs@ [dict get {
HC {hash, &context}
CH {&context, hash}
} $forder]
# Introspection: digest size, and references
critcl::cconst ::ak::hash::${hash}::size int $hsize
critcl::cconst ::ak::hash::${hash}::references char* "\"[lrange $refs 0 end]\""
# Common channel code for the hash.
critcl::ccode [string map $map {
static Tcl_Obj* do_chan_@@ (Tcl_Interp* ip, Tcl_Channel chan, _options* opts) {
unsigned char hash [@hsize@];
@context@ context;
if (opts->offset > 0) {
/* Skip forward in the channel. By comparing the positions before and after
* we determine of we skipped over the end of the channel, or not
*/
Tcl_WideInt before = Tcl_Tell (chan);
Tcl_Seek (chan, opts->offset, SEEK_CUR);
Tcl_WideInt after = Tcl_Tell (chan);
if (after < (before + opts->offset)) {
/* Beyond eof, treat as empty input */
@init@ (&context);
goto done;
}
}
@init@ (&context);
if (opts->length == 0) goto done;
unsigned char* receiver = Tcl_Alloc (CHUNK_SIZE);
Tcl_SetChannelBufferSize(chan, BUF_SIZE); /* OK tcl9 */
if (opts->length > 0) {
/* Loop limited by end of channel and user-specified maximum */
while (!Tcl_Eof (chan) && (opts->length > 0)) {
Tcl_Size toget = (opts->length < CHUNK_SIZE ? opts->length : CHUNK_SIZE);
Tcl_Size received = Tcl_Read (chan, receiver, toget); /* OK tcl9 */
if (!received) continue;
@update@ (&context, receiver, received);
opts->length -= received;
}
} else {
/* Loop limited only by end of channel */
while (!Tcl_Eof (chan)) {
Tcl_Size received = Tcl_Read (chan, receiver, CHUNK_SIZE); /* OK tcl9 */
if (!received) continue;
@update@ (&context, receiver, received);
}
}
Tcl_Free (receiver);
done:
@final@ (@fargs@);
return Tcl_NewByteArrayObj (hash, @hsize@); /* OK tcl9 */
}
}]
# Hash the data in a file.
critcl::cproc ::ak::hash::${hash}::path {
Tcl_Interp* ip
char* path
object args
} object0 [string map $map {
_options opts;
if (TCL_OK != handle_options (&opts, ip, (Tcl_Obj**) args.v, args.c)) { return 0; }
Tcl_Channel chan = Tcl_OpenFileChannel (ip, path, "rb", 0);
if (chan == NULL) {
return NULL;
}
Tcl_Obj* r = do_chan_@@ (ip, chan, &opts);
Tcl_Close (ip, chan);
return r;
}]
# Hash the data in a channel, from current position to end of channel
critcl::cproc ::ak::hash::${hash}::channel {
Tcl_Interp* ip
channel chan
object args
} object0 [string map $map {
_options opts;
if (TCL_OK != handle_options (&opts, ip, (Tcl_Obj**) args.v, args.c)) { return 0; }
return do_chan_@@ (ip, chan, &opts);
}]
# Hash the data in a Tcl ByteArray
critcl::cproc ::ak::hash::${hash}::string {
Tcl_Interp* ip
bytes str
object args
} object0 [string map $map {
_options opts;
if (TCL_OK != handle_options (&opts, ip, (Tcl_Obj**) args.v, args.c)) { return 0; }
if (opts.offset > 0) {
if (opts.offset > str.len) {
/* Hashing behind the end of the string ~~ Hashing of empty string */
str.len = 0;
} else {
/* Skip forward over the unwanted prefix */
str.s += opts.offset;
str.len -= opts.offset;
}
}
if (opts.length >= 0) {
if (opts.length < str.len) {
/* Truncate the unwanted suffix */
str.len = opts.length;
}
}
unsigned char hash [@hsize@];
@context@ context;
@init@ (&context);
@update@ (&context, str.s, str.len);
@final@ (@fargs@);
return Tcl_NewByteArrayObj (hash, @hsize@); /* OK tcl9 */
}]
set map {}
}
# # ## ### ##### ######## #############
return