Mercurial > hg > dml-open-cliopatria
comparison cpack/dml/lib/compression.pl @ 0:718306e29690 tip
commiting public release
author | Daniel Wolff |
---|---|
date | Tue, 09 Feb 2016 21:05:06 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:718306e29690 |
---|---|
1 /* Part of DML (Digital Music Laboratory) | |
2 Copyright 2014-2015 Samer Abdallah, University of London | |
3 | |
4 This program is free software; you can redistribute it and/or | |
5 modify it under the terms of the GNU General Public License | |
6 as published by the Free Software Foundation; either version 2 | |
7 of the License, or (at your option) any later version. | |
8 | |
9 This program is distributed in the hope that it will be useful, | |
10 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 GNU General Public License for more details. | |
13 | |
14 You should have received a copy of the GNU General Public | |
15 License along with this library; if not, write to the Free Software | |
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
17 */ | |
18 | |
19 :- module(compression, | |
20 [ with_transducer/3 | |
21 , stream_length/1 | |
22 , print_delimited_dcg/1 | |
23 , complexity_method/1 | |
24 , complexity/3 | |
25 , conditional_complexity/4 | |
26 , min_complexity/3 | |
27 , min_conditional_complexity/4 | |
28 , ncd/4 | |
29 , conditional_ncd/5 | |
30 , write_bytes/1 | |
31 , length_prob/3 | |
32 , with_stream_encoding/3 | |
33 , with_zlib_stream/1 | |
34 , zsync//1 | |
35 ]). | |
36 | |
37 /** <module> Tools for applying compression programs to abitrary streams. | |
38 | |
39 This module provides a framework for applying a range of compression and | |
40 delta compression programs to arbitrary streams generated by Prolog code. | |
41 In particular, the predicates complexity/3 and conditional_complexity/4 | |
42 use compression programs to estimate Kolmogorov complexity and conditional | |
43 complexity. | |
44 | |
45 Compression programs are invoked by a collection of shell scripts included | |
46 with this software, in the scripts/compression subdirectory of the dml cpack. | |
47 The method to be used is determined by a term of type compression_method: | |
48 == | |
49 compression_method ---> id % no compression | |
50 ; compress(c_method) | |
51 ; diff(d_method,pred). | |
52 c_method ---> lzma ; gz ; bz. | |
53 | |
54 d_method ---> bsdiff | |
55 ; xdelta % using xdelta3 program | |
56 ; vcdiff % using open-vcdiff | |
57 ; vczip % using vczip tools from AT&T | |
58 ; xdiff % using binary to text encoding and diff | |
59 ; zzd(c_method, d_method) | |
60 ; zzcd(c_method, d_method) | |
61 ; dz(d_method, c_method). | |
62 == | |
63 | |
64 Currently we do not subtract an known overheads associated with redundant | |
65 information in compression program outputs, such as 'magic bytes' in a header | |
66 used to enable tools to recognise which program was used to compress a given file. | |
67 */ | |
68 | |
69 :- use_module(library(settings)). | |
70 :- use_module(library(memo)). | |
71 :- use_module(library(process)). | |
72 :- use_module(library(fileutils)). | |
73 :- use_module(library(swipe)). | |
74 :- use_module(library(dcg_core)). | |
75 :- use_module(library(dcg_codes)). | |
76 :- use_module(library(zlib)). | |
77 | |
78 user:file_search_path(compression,dml(scripts/compression)). | |
79 | |
80 :- initialization time(memo_attach(memo(compression),[])). | |
81 | |
82 %% print_delimited_dcg(+P:phrase(list(code))) is det. | |
83 % | |
84 % Runs the DCG phrase to generate a list of character codes, and then | |
85 % print the textually encoded length of the resulting list, followed by | |
86 % a carriage return, followed by the characters themselves. Thus, the resulting | |
87 % sequence of bytes is self-delimiting. | |
88 print_delimited_dcg(Phrase) :- | |
89 phrase(Phrase,Codes), | |
90 length(Codes,Length), | |
91 format('~d\n~s',[Length,Codes]). | |
92 | |
93 :- meta_predicate | |
94 complexity(+,0,-), | |
95 min_complexity(-,0,-), | |
96 conditional_complexity(+,0,0,-), | |
97 min_conditional_complexity(-,0,0,-), | |
98 conditional_ncd(+,0,0,0,-), | |
99 ncd(+,0,0,-). | |
100 | |
101 :- meta_predicate with_zlib_stream(//). | |
102 | |
103 %% complexity(+Method:compression_method, +Writer:pred, -K:nonneg) is det. | |
104 %% complexity(-Method:compression_method, +Writer:pred, -K:nonneg) is nondet. | |
105 %% complexity(-Method:compression_method, -Writer:pred, -K:nonneg) is nondet. | |
106 % | |
107 % Estimates the Kolmogorov complexity in bits of the sequence of bytes written to the | |
108 % current output stream by the goal Writer. If Writer is unbound on entry, then | |
109 % browses the set of previously computed results, which are stored in a volatile memo. | |
110 % | |
111 % Method can be =|id|= for no compression, or =|compress(M)|= for compression using | |
112 % program M. If Method is a ground term of the form =|diff(M,RefWriter)|=, then | |
113 % complexity is estimated using a delta compression with respect to the bytes written | |
114 % by RefWriter. Methods are described in the module header. Conditional complexity | |
115 % can more conveniently be estimated using conditional_complexity/4. | |
116 complexity(Method,Mod:Writer,K) :- | |
117 ( nonvar(Writer), complexity_method(Method), ground(Method) | |
118 *->complexity1(Method,Mod:Writer,K) | |
119 ; browse(complexity1(Method,Mod:Writer,K)) | |
120 ). | |
121 | |
122 execable(A) :- var(A), !, fail. | |
123 execable((A,B)) :- !, execable(A), execable(B). | |
124 execable(_). | |
125 | |
126 :- volatile_memo min_complexity(-ground,+callable,-number). | |
127 min_complexity(Best,Writer,KMin) :- | |
128 aggregate(min(K,Meth),complexity(Meth,Writer,K),min(KMin,Best)). | |
129 | |
130 %% conditional_complexity(+M:cc_method, +RefWriter:pred, +Writer:pred, -K:nonneg) is det. | |
131 % | |
132 % Estimate conditional Kolmogorov complexity of byte sequence produced by Writer | |
133 % given bit sequence produced by RefWriter, using one of several methods: | |
134 % == | |
135 % cc_method ---> id % no compression, complexity is length of data from Writer | |
136 % ; compress(c_method) % ignore reference sequence and compress | |
137 % ; delta(d_method) % do delta compression using given method | |
138 % ; naive(c_method). % use naive estimate using concatentation method | |
139 % == | |
140 % The naive estimate of K(x|y) is C(yx) - C(y), where C(.) is compressed length and | |
141 % xy is simply x concatenated with y. It may give nonsensical zero results. | |
142 % It is not the length of any concrete message that can produce x given y. | |
143 conditional_complexity(Method,Ref,Writer,K) :- | |
144 ( Method=delta(Diff), M=diff(Diff,Ref) | |
145 % ; Method=compress(C), M=compress(C) | |
146 % ; Method=compress(zlib), M=zlib | |
147 ; Method=delta(zlib), M=zlib(Ref) | |
148 ; Method=delta(dlzma), M=dlzma(Ref) | |
149 % ; Method=id, M=id | |
150 ), | |
151 complexity(M,Writer,K). | |
152 | |
153 conditional_complexity(naive(C),Ref,Writer,K) :- | |
154 complexity(compress(C),Ref,KY), | |
155 complexity(compress(C),(Ref,Writer),KXY), | |
156 K is KXY - KY. | |
157 | |
158 ncd(naive(C),Writer1,Writer2,D) :- | |
159 complexity(compress(C),Writer1,K1), | |
160 complexity(compress(C),Writer2,K2), | |
161 complexity(compress(C),(Writer1,Writer2),K12), | |
162 D is (K12 - min(K1,K2))/max(K1,K2). | |
163 | |
164 ncd(CM-DM,Writer1,Writer2,D) :- | |
165 complexity(CM,Writer1,K1), | |
166 complexity(CM,Writer2,K2), | |
167 conditional_complexity(DM,Writer1,Writer2,K2g1), | |
168 conditional_complexity(DM,Writer2,Writer1,K1g2), | |
169 D is max(min(K1,K1g2)/K1, min(K2,K2g1)/K2). | |
170 | |
171 ncd(opt,Writer1,Writer2,D) :- | |
172 min_complexity(_,Writer1,K1), | |
173 min_complexity(_,Writer2,K2), | |
174 min_conditional_complexity(_,Writer1,Writer2,K2g1), | |
175 min_conditional_complexity(_,Writer2,Writer1,K1g2), | |
176 D is max(min(K1,K1g2)/K1, min(K2,K2g1)/K2). | |
177 | |
178 conditional_ncd(naive(Method),Ref,Writer1,Writer2,D) :- | |
179 conditional_complexity(Method,Ref,Writer1,K1), | |
180 conditional_complexity(Method,Ref,Writer2,K2), | |
181 conditional_complexity(Method,Ref,(Writer1,Writer2),K12), | |
182 D is (K12 - min(K1,K2))/max(K1,K2). | |
183 | |
184 conditional_ncd(DM,Ref,Writer1,Writer2,D) :- | |
185 conditional_complexity(DM,Ref,Writer1,K1), | |
186 conditional_complexity(DM,Ref,Writer2,K2), | |
187 conditional_complexity(DM,(Ref,Writer1),Writer2,K2g1), | |
188 conditional_complexity(DM,(Ref,Writer2),Writer1,K1g2), | |
189 D is max(min(K1,K1g2)/K1, min(K2,K2g1)/K2). | |
190 | |
191 :- volatile_memo min_conditional_complexity(-ground,+callable,+callable,-number). | |
192 min_conditional_complexity(Best,Ref,Writer,KMin) :- | |
193 aggregate(min(K,Meth),(conditional_complexity(Meth,Ref,Writer,K),Meth\=naive(_)), min(KMin,Best)). | |
194 | |
195 %% complexity_method(-M:compression_method) is nondet. | |
196 complexity_method(diff(Diff,_)) :- command(diff0(Diff,library('swipe.pl')),_,_). | |
197 complexity_method(compress(Comp)) :- command(compress(Comp),_,_). | |
198 complexity_method(id). | |
199 complexity_method(zlib). | |
200 complexity_method(zlibi). | |
201 complexity_method(zlib(_)). | |
202 complexity_method(dlzma(_)). | |
203 | |
204 :- persistent_memo complexity1(+ground,+callable,-number). | |
205 complexity1(id,Writer,K) :- with_transducer(cat,Writer,stream_length(K)). | |
206 complexity1(compress(Meth),Writer,K) :- with_transducer(compress(Meth), Writer, stream_length(K)). | |
207 complexity1(diff(Diff,RefWriter),Writer,K) :- | |
208 with_temp_files([F1], | |
209 ( with_output_to_file(F1,RefWriter), | |
210 with_transducer(diff0(Diff,F1), Writer, stream_length(K)))). | |
211 complexity1(zlib,Writer,K) :- | |
212 with_zlib_stream(call_zsync(Writer,K)). | |
213 | |
214 complexity1(zlibi,Writer,K) :- | |
215 with_zlib_stream((call_zsync(write_bytes([255]),_), call_zsync(Writer,K))). | |
216 | |
217 complexity1(zlib(Ref),Writer,K) :- | |
218 with_zlib_stream((call_zsync(Ref,_), call_zsync(Writer,K))). | |
219 | |
220 complexity1(dlzma(RefWriter),Writer,K) :- | |
221 with_temp_files([F1], | |
222 ( with_output_to_file(F1,RefWriter), | |
223 with_transducer(dlzma(F1,['-b0']), Writer, read_line_to_string(current_input,KString)))), | |
224 number_string(K,KString). | |
225 | |
226 | |
227 subs(_,_,T1,T2) :- var(T1), !, T1=T2. | |
228 subs(Old,New,T1,T2) :- T1==Old, !, T2=New. | |
229 subs(Old,New,T1,T2) :- | |
230 T1 =.. [F | Args1], | |
231 maplist(subs(Old,New),Args1,Args2), | |
232 T2 =.. [F | Args2]. | |
233 | |
234 | |
235 % this is hopeless... | |
236 length_prob(Meth,L,Prob) :- | |
237 length(X,L), | |
238 aggregate( sum(2**(-8*B)), | |
239 X^(maplist(between(0,255),X), | |
240 with_transducer(compress(Meth), write_bytes(X), stream_length(B)), | |
241 format(user_error, '\r~w : ~w', [X,B]) | |
242 ), | |
243 Prob). | |
244 | |
245 %% write_bytes(+Bytes:list(between(0,255))) is det. | |
246 % Writes a sequence of bytes to current_output. | |
247 write_bytes(Bytes) :- with_stream_encoding(current_output,octet,maplist(put_code,Bytes)). | |
248 | |
249 | |
250 % -------------------------------- Using ZLIB ------------------------------ | |
251 | |
252 | |
253 with_zlib_stream(Phrase) :- | |
254 setup_call_cleanup( | |
255 open_null_stream(Out), | |
256 setup_call_cleanup( | |
257 zopen(Out,ZOut,[level(9),close_parent(false)]), | |
258 with_output_to(ZOut,call_dcg(Phrase,Out-0,_)), | |
259 close(ZOut)), | |
260 close(Out)). | |
261 | |
262 zpos(Pos,Out-Pos,Out-Pos). | |
263 zsync(Bits,Out-Pos0,Out-Pos1) :- | |
264 flush_output, | |
265 byte_count(Out,Pos1), | |
266 ( Pos1=Pos0 -> Bits=0 % no data since last sync | |
267 ; Bits is 8*((Pos1-Pos0)-4)-(3+3) % subtract estimate of final type 0 block. | |
268 ). | |
269 | |
270 call_zsync(Goal,Delta) --> {call(Goal)}, zsync(Delta). | |
271 | |
272 | |
273 % ------ Method comparison ----- | |
274 evaluate_delta(KRel,Method) :- | |
275 call_with_mode(browse, setof(K-Meth,conditional_complexity(Meth,_:_,_:_,K),KMeths)), | |
276 memberchk(_-delta(_),KMeths), | |
277 exclude(eval_exclude,KMeths,KMeths1), | |
278 KMeths1 = [KBest-_|_], | |
279 member(K-Method,KMeths1), | |
280 KRel is K/KBest. | |
281 | |
282 :- public method_rankings/2. | |
283 method_rankings(Method,Scores) :- | |
284 setof( Method-KRels, | |
285 bagof(KRel, evaluate_delta(KRel,Method), KRels), | |
286 AllScores), | |
287 member(Method-Scores,AllScores). | |
288 | |
289 eval_exclude(_-Method) :- exclude_method(Method). | |
290 exclude_method(naive(_)). | |
291 exclude_method(delta(DMethod)) :- exclude_dmethod(DMethod). | |
292 exclude_dmethod(xdiff). | |
293 exclude_dmethod(dz(DM,_)) :- exclude_dmethod(DM). | |
294 exclude_dmethod(zzcd(_,DM)) :- exclude_dmethod(DM). | |
295 exclude_dmethod(zzd(_,DM)) :- exclude_dmethod(DM). | |
296 | |
297 delta_method(compress(C),compress(C)). | |
298 delta_method(delta(M),delta(M)). | |
299 delta_method(id,id). | |
300 | |
301 | |
302 % ----------------------- Compression framework -------------------------------------- | |
303 | |
304 | |
305 swipe:def(Pipe,Def) :- def(Pipe,Def). | |
306 | |
307 def( findcat(Dir), sh(0 >> $_, '~s ~s',[compression(findcat)+execute, file(Dir,[file_type(directory)])])). | |
308 def( humdump(Dir), findcat(Dir) >> sh($hum >> $hum,'rid -G')). | |
309 def( length, sh($_ >> $number, 'wc -c')). | |
310 def( prepend(File^T), sh($T >> $T,'cat ~s -',[File+read])). | |
311 def( prepend(_<Pipe), Pipe * cat). % sh($T >> $T,'cat ~s -',[T<Pipe])). | |
312 def( unprepend(File^T), sh($T >> $T, 'tail -c +$((1+~s))',[$(File^T :> length)])). | |
313 def( unprepend(T<Pipe), sh($T >> $T, 'tail -c +$((1+~s))',[$(Pipe >> length)])). | |
314 def( compress(Method), sh( $X >> $z(Method,X), Cmd)) :- compressor(Method,Cmd,_). | |
315 def( decompress(Method), sh( $z(Method,X) >> $X, Cmd)) :- compressor(Method,_,Cmd). | |
316 | |
317 def( diff0(Diff,Ref), diff(Diff,Ref)). | |
318 % def( diff0(dz(Diff,Comp),Ref), diff(Diff,Ref) >> compress(Comp)). | |
319 def( diff(Diff,Ref), diff(Diff, encode, Ref+read)). | |
320 % def( diff(zzd(Comp,Diff),Ref), compress(Comp) >> buf(2,diff(Diff, encode, T<(Ref^T :> compress(Comp))))). | |
321 def( diff(zzcd(Comp,Diff),Ref), prepend(Ref^T) >> compress(Comp) >> buf(2,diff(Diff, encode, _<(Ref^T :> compress(Comp))))). | |
322 | |
323 def( patch(Method,Ref), diff(Method, decode, Ref+read)). | |
324 def( patch(zzd(Comp,Diff),Ref), buf(2,diff(Diff, decode, _<(Ref^_ :> compress(Comp)))) >> decompress(Comp)). | |
325 def( patch(zzcd(Comp,Diff),Ref), buf(2,diff(Diff, decode, _<(Ref^T :> compress(Comp)))) >> decompress(Comp) >> unprepend(Ref^T)). | |
326 | |
327 def( diff(Method,encode,RefSource), sh( $X >> $dz(Method,X), [compression(Script)+execute, encode, RefSource | Args])) :- differ(Method,Script,Args,_). | |
328 def( diff(Method,decode,RefSource), sh( $dz(Method,X) >> $X, [compression(Script)+execute, decode, RefSource | Args])) :- differ(Method,Script,_,Args), Method\=zvcz. | |
329 def( buf(N,Pipe), sh(Type,'~s ~d ~s',[compression(bufs)+execute, \N, \Cmd])) :- command(Pipe,Type,Cmd). | |
330 | |
331 def( diff1(Diff,Ref), diff2(Diff, Ref)). | |
332 def( diff1(zzd(Comp,Diff),Ref), compress(Comp) >> diff2(Diff, Ref >> compress(Comp))). | |
333 def( diff1(zzcd(Comp,Diff),Ref), prepend(_<Ref) >> compress(Comp) >> diff2(Diff, Ref >> compress(Comp))). | |
334 def( patch1(Diff,Ref), patch2(Diff, Ref)). | |
335 def( patch1(zzd(Comp,Diff),Ref), patch2(Diff, Ref >> compress(Comp)) >> decompress(Comp)). | |
336 def( patch1(zzcd(Comp,Diff),Ref), patch2(Diff, Ref >> compress(Comp)) >> decompress(Comp) >> unprepend(_<Ref)). | |
337 | |
338 def( diff2(Method,Ref), sh( $X >> $dz(Method,X), [compression(encode)+execute, Script, X<Ref | Args])) :- differ(Method,Script,Args,_). | |
339 def( patch2(Method,Ref), sh( $dz(Method,X) >> $X, [compression(decode)+execute, Script, X<Ref | Args])) :- differ(Method,Script,_,Args), Method\=zvcz. | |
340 | |
341 def( dlzma(Ref,Opts), sh($X >> $dz(dlzma,X), [compression(dlzma)+execute | Args])) :- | |
342 append(Opts,[Ref],Args). | |
343 | |
344 % compressor(xz,xz,xzcat). | |
345 compressor(lzma,lzma,lzcat). | |
346 compressor(xzraw,'xz -q -F raw','xzcat -F raw'). | |
347 compressor(gz,gzip,zcat). | |
348 compressor(bz,bzip2,bzcat). | |
349 | |
350 contains(Diff,Diff). | |
351 contains(dz(D1,_),D2) :- contains(D1,D2). | |
352 contains(zzd(_,D1),D2) :- contains(D1,D2). | |
353 contains(zzcd(_,D1),D2) :- contains(D1,D2). | |
354 | |
355 | |
356 % :- setting(max_vczip_chain_length, nonneg, 2, 'Maximum length of vczip processing chain'). | |
357 | |
358 differ(Method,Exec,[],[]) :- differ(Method,Exec). | |
359 differ(Method-Opts, Exec, EncArgs, []) :- differ(Method,Exec), options_for(Method,Opts,EncArgs). | |
360 differ(vczip(vcdiff),zvcz,['-Vcdiff'],[]). | |
361 differ(vczip(delta),zvcz,['-mdelta'],[]). | |
362 differ(vczip(sieve),zvcz,['-msieve.delta'],[]). | |
363 % differ(vczip(Delta,Chain,Encode),zvcz,[\Codes],[]) :- | |
364 % setting(max_vczip_chain_length,N), | |
365 % between(1,N,L), | |
366 % length(Chain,L), | |
367 % vczip_chain(Delta,Chain,Encode,Codes,[]). | |
368 | |
369 % differ(bsdiff,zbs). | |
370 % differ(xdelta,zxd). | |
371 differ(vcdiff,zvcd). | |
372 % differ(vczip,zvcz). | |
373 % differ(xdiff,zdiff). | |
374 | |
375 options_for(Method,Opts,Args) :- | |
376 Opts=[_|_], | |
377 setof(opt(Opt,Gen,A1,A2),method_option(Method,Opt,Gen,A1,A2),Possible), | |
378 seqmap(maybe_option,Possible,Opts-Args,[]-[]). | |
379 | |
380 maybe_option(_) --> []. | |
381 maybe_option(opt(Opt,Gen,A1,A2),[Opt|Opts]-A1,Opts-A2) :- call(Gen). | |
382 | |
383 method_option(xdelta, secondary(A), member(A,[djw,fgk])) --> ['-S',A]. | |
384 method_option(vcdiff, target_matches, true) --> ['-target_matches']. | |
385 | |
386 % vczip_chain(Delta,Chain,Encode) --> | |
387 % "-m", | |
388 % vczip_delta(Delta), ",", | |
389 % seqmap_with_sep(",",vczip_transform,Chain), ",", | |
390 % vczip_encode(Encode). | |
391 | |
392 % vczip_delta(delta) --> "delta". | |
393 % vczip_delta(sieve) --> "sieve.delta". | |
394 | |
395 % vczip_transform(bwt) --> "bwt". | |
396 % vczip_transform(mtf) --> "mtf". | |
397 % vczip_transform(rle) --> "rle". | |
398 % vczip_transform(rle(N)) --> "rle.", {between(0,1,N)}, at(N). | |
399 | |
400 % vczip_encode(huffman) --> "huffman". | |
401 % vczip_encode(huffgroup) --> "huffgroup". | |
402 % vczip_encode(huffpart) --> "huffpart". | |
403 | |
404 | |
405 compressor_overhead(gz,18). % see http://www.onicos.com/staff/iz/formats/gzip.html, https://tools.ietf.org/html/rfc1952 | |
406 compressor_overhead(bz,4). % http://en.wikipedia.org/wiki/Bzip2 (need to know number of blocks for better estimate) | |
407 compressor_overhead(lzma,0). % http://svn.python.org/projects/external/xz-5.0.3/doc/lzma-file-format.txt | |
408 | |
409 % ------------------------- Pipe and file tools ---------------------------------- | |
410 | |
411 splice_in(Pipe) :- with_pipe_input(S,Pipe,copy_stream_data(current_input,S)). | |
412 splice_out(Pipe) :- | |
413 with_pipe_output(S,Pipe, | |
414 ( with_stream_encoding(S,octet, | |
415 with_stream_encoding(current_output,octet, | |
416 copy_stream_data(S,current_output))))). | |
417 | |
418 :- op(1050,xfy,&). | |
419 | |
420 | |
421 %% with_transducer(+P:pipe, +Writer:pred, +Reader:pred) is det. | |
422 % Runs a pipeline defined using the framework provided by library(swipe), | |
423 % while concurrently sending data written by Writer to the process | |
424 % on its standard input and making data from the standard output of the | |
425 % process available to Reader on current_input. | |
426 :- meta_predicate with_transducer(+,0,0). | |
427 with_transducer(Pipe,Writer,Reader) :- | |
428 with_pipe_io( To-From, Pipe, | |
429 ( call_cleanup(with_output_to(To,Writer), close(To)) | |
430 & call_cleanup(with_input_from(From,Reader), close(From)) | |
431 )). | |
432 | |
433 | |
434 A & B :- concurrent(2, [A,B], []). | |
435 | |
436 %% with_stream_encoding(+S:stream,+E:encoding,+G:pred) is det. | |
437 % Call goall G with encoding of stream S temporarily set to E. Encoding | |
438 % is restored afterwards. | |
439 :- meta_predicate with_stream_encoding(+,+,0). | |
440 with_stream_encoding(S,Enc,Goal) :- | |
441 stream_property(S,encoding(Old)), | |
442 setup_call_cleanup(set_stream(S,encoding(Enc)), Goal, set_stream(S,encoding(Old))). | |
443 | |
444 %% stream_length(-L:natural) is det. | |
445 % Reads all the data available on Prolog stream current_input and returns the | |
446 % number of bits consumed. | |
447 stream_length(S,Bits) :- with_input_from(S,stream_length(Bits)). | |
448 stream_length(Bits) :- | |
449 with_stream_encoding(current_input,octet,accum_length(0,Bytes)), | |
450 Bits is 8*Bytes. | |
451 | |
452 accum_length(L,L) :- at_end_of_stream, !. | |
453 accum_length(L1,L3) :- | |
454 read_pending_input(current_input, Codes, []), | |
455 length(Codes,L), | |
456 L2 is L1 + L, | |
457 accum_length(L2,L3). | |
458 | |
459 with_temp_files(Files,Goal) :- | |
460 must_be(list(var),Files), | |
461 setup_call_cleanup( | |
462 maplist(tmp_file_stream(binary),Files,Streams), | |
463 (maplist(close,Streams), call(Goal)), | |
464 maplist(delete_file,Files)). |