-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdatabase_internal_transput.pro
135 lines (113 loc) · 4.21 KB
/
database_internal_transput.pro
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
:- module(database_internal_transput, [
db_open_file/3,
db_close/1,
db_flush/1,
db_seek/2,
db_read/2,
db_write/2
]).
:- use_module(library(clpfd)).
:- use_module(library(error)).
:- use_module('./database_internal_dcg.pro').
:- use_module('./database_internal_pure.pro').
:- use_module('./transput.pro').
/** <module> database.pro internals; do not use
*/
/**
handle_streams_type(Handle, ReadStream, WriteStream, Type).
handle_stride(Handle, Stride).
handle_elemofs_byteofs(Handle, ElemOffset, ByteOffset).
handle_elemcount_bytecount(Handle, ElemCount, ByteCount).
Internal representation used by low-level transput predicates such as db_open_file/3.
Stride is the number of bytes per element.
ByteOffset is ElemOffset multiplied by Stride.
At least one of ByteOffset or ElemOffset must be ground.
*/
handle_streams_type(Handle, SR, SW, Type) :-
Handle = handle(SR,SW,Type,Stride),
type_bytecount(Type,Stride).
handle_streams(Handle, SR, SW) :- arg(1, Handle, SR), arg(2, Handle, SW).
handle_stride(Handle, Stride) :- arg(4, Handle, Stride).
handle_streams_stride(Handle, SR, SW, Stride) :-
handle_streams(Handle, SR, SW),
handle_stride(Handle, Stride).
handle_streams_type_stride(Handle, SR, SW, Type, Stride) :-
handle_streams_type(Handle, SR, SW, Type),
handle_stride(Handle, Stride).
handle_elemofs_byteofs(H, E, B) :- handle_stride(H, S), B #= E * S.
handle_elemcount_bytecount(H, E, B) :- handle_stride(H, S), B #= E * S.
/** db_open_file(++Path, ++Type, --Handle)
Open a database file.
Type is defined in type_value//2.
Important:
- If you change Type, make sure that your change is backward-compatible with the data that is already stored in the file.
Non-portability:
- This assumes that SWI-Prolog will lock the file.
This uses the SWI-Prolog-specific lock(read) and lock(write) option of open/4.
- SWI-Prolog os/pl-file.c/openStream calls os/pl-stream.c/Sopen_file
which never passes O_RDWR to open(2),
so we have to open two streams: one for reading, one for "updating" (writing without truncating the file).
- ISO Prolog open/4 does not have a way of opening a file for writing without truncating a file.
What the hell.
See also:
seek/4, read_string/3.
*/
db_open_file(Path, Type, Handle) :-
must_be(ground, Path),
must_be(ground, Type),
(type_bytecount(Type, _) -> true ; type_error(type, Type)),
catch_all(
[
open(Path, update, SW, [type(binary), lock(write), wait(false)]),
open(Path, read, SR, [type(binary), lock(read), wait(false)])
], Es),
(Es = [] -> handle_streams_type(Handle, SR, SW, Type)
; (ground(SR) -> close(SR) ; true),
(ground(SW) -> close(SW) ; true),
rethrow(Es)
).
/**
db_close(++Handle).
db_flush(++Handle).
db_seek(++Handle, ++ElemOffset).
db_write(++Handle, ++Elems).
Low-level transput.
*/
db_close(Handle) :-
handle_streams(Handle, SR, SW),
catch_all([close(SR),close(SW)], Es),
(Es = [] -> true ; rethrow(Es)).
db_flush(Handle) :-
handle_streams(Handle, SR, SW),
flush_output(SR),
flush_output(SW).
db_seek(Handle, ElemOffset) :-
handle_streams(Handle, SR, SW),
handle_elemofs_byteofs(Handle, ElemOffset, ByteOffset),
seek(SR, ByteOffset, bof, _),
seek(SW, ByteOffset, bof, _).
db_write(Handle, Elems) :-
must_be(list, Elems),
length(Elems, ElemCount),
handle_streams_type(Handle, _, SW, Type),
handle_elemcount_bytecount(Handle, ElemCount, ByteCount),
type_values_bytes_remain(Type, Elems, Bytes, []),
assertion(length(Bytes, ByteCount)),
put_bytes(SW, Bytes).
/** db_read(++Handle, +Elems)
The length of Elems must be bound.
@error truncated_last_record(Remain)
if the last record is truncated (its size is not an integer multiple of stride).
This may be due to a previous write error.
@error instantiation_error
if Elems is not bound
*/
db_read(Handle, Elems) :-
must_be(list, Elems),
length(Elems, ElemCount),
handle_streams_type(Handle, SR, _, Type),
handle_elemcount_bytecount(Handle, ElemCount, ByteCount),
length(Bytes, ByteCount),
get_bytes(SR, Bytes),
type_values_bytes_remain(Type, Elems, Bytes, Remain),
(Remain = [] -> true ; throw(error(truncated_last_record(Remain), _))).