1 |
<?php |
2 |
/* |
3 |
OpenIsis - an open implementation of the CDS/ISIS database |
4 |
Version 0.8.x (patchlevel see file Version) |
5 |
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
6 |
|
7 |
This library is free software; you can redistribute it and/or |
8 |
modify it under the terms of the GNU Lesser General Public |
9 |
License as published by the Free Software Foundation; either |
10 |
version 2.1 of the License, or (at your option) any later version. |
11 |
|
12 |
This library is distributed in the hope that it will be useful, |
13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
Lesser General Public License for more details. |
16 |
|
17 |
You should have received a copy of the GNU Lesser General Public |
18 |
License along with this library; if not, write to the Free Software |
19 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
|
21 |
see README for more information |
22 |
EOH */ |
23 |
|
24 |
// $Id: File.php,v 1.4 2003/06/13 19:35:24 kripke Exp $ |
25 |
|
26 |
|
27 |
/** |
28 |
the magic for a 4/4/0 little endian isis xref file. |
29 |
http://OpenIsis.org/openisis/doc/Serialized |
30 |
*/ |
31 |
define( 'ISIS_FILE_MAGIC', "ISIXD\0:)" ); |
32 |
/** |
33 |
The flat file database. |
34 |
It does not know anything about isis records, |
35 |
it's just dealing with the string representation. |
36 |
|
37 |
58971 records (unesb), 29442129 bytes flat (34565120 as .mst) |
38 |
mkxrf: 8.1 sec (7280 recs / sec) |
39 |
read all (just the string): 3.48 sec (~17.000 recs / sec) |
40 |
read 10.000, creating recs: 3.95 sec |
41 |
read 1.000, creating recs, printing: ~2 sec |
42 |
|
43 |
@version $Revision: 1.4 $ |
44 |
@license LGPL |
45 |
@package Isis |
46 |
*/ |
47 |
class Isis_File { |
48 |
var $name; |
49 |
var $mst; |
50 |
var $xrf = 0; |
51 |
var $w; |
52 |
/** number of records == number of last record |
53 |
*/ |
54 |
var $len = 0; |
55 |
|
56 |
function Isis_File ( $name, $write = 0 ) |
57 |
{ |
58 |
$this->name = $name; |
59 |
$this->w = $write; |
60 |
$this->mst = fopen($name.'.txt', $write ? 'a+' : 'r'); |
61 |
if ( ! $this->mst ) |
62 |
return; |
63 |
flock($this->mst, $write ? LOCK_EX : LOCK_SH); |
64 |
$s = fstat($this->mst); |
65 |
$mtime = $s['mtime']; |
66 |
if ( file_exists($name.'.ptr') ) // avoid annoying warning |
67 |
$this->xrf = fopen($name.'.ptr', $write ? 'r+' : 'r'); |
68 |
if (!$this->xrf) |
69 |
$remake = 1; |
70 |
else { |
71 |
$s = fstat($this->xrf); |
72 |
$magic = fread($this->xrf, 8); |
73 |
$remake = $s['mtime'] < $mtime || ISIS_FILE_MAGIC != $magic; |
74 |
if ( !$remake ) |
75 |
$this->len = $s['size'] / 8 - 1; |
76 |
if ( $remake && !$write ) { |
77 |
fclose($this->xrf); |
78 |
$this->xrf = 0; |
79 |
} |
80 |
} |
81 |
if ( !$this->xrf && !($this->xrf = fopen($name.'.ptr','w+')) ) { |
82 |
fclose($this->mst); |
83 |
return; |
84 |
} |
85 |
if ( $remake ) |
86 |
$this->mkxref(); |
87 |
} |
88 |
|
89 |
|
90 |
function mkxref () |
91 |
{ |
92 |
fseek($this->mst, 0); |
93 |
ftruncate($this->xrf, 0); |
94 |
fseek($this->xrf, 0); |
95 |
fwrite( $this->xrf, ISIS_FILE_MAGIC, 8 ); |
96 |
$mfn = 0; |
97 |
$lines = 0; |
98 |
for (;;) { |
99 |
$line = fgets($this->mst); |
100 |
$end = !is_string($line) || '' == $line; |
101 |
if ( $end || "\n" == $line{0} ) { |
102 |
$now = ftell($this->mst); |
103 |
$len = $now - $pos - 1; |
104 |
if ( $len && !$end ) |
105 |
$len--; |
106 |
if ($mfn) { |
107 |
$xrf = pack('VV', $pos, $len); |
108 |
// echo "mfn $mfn pos $pos len $len\n"; |
109 |
if ( 8 != ($foo = fwrite($this->xrf, $xrf)) ) |
110 |
echo "FOO! $foo\n"; |
111 |
if ($this->len < $mfn) |
112 |
$this->len = $mfn; |
113 |
} |
114 |
if ($end) |
115 |
break; |
116 |
$mfn++; |
117 |
$pos = $now; |
118 |
$lines = 0; |
119 |
continue; |
120 |
} |
121 |
if ( ! $lines |
122 |
&& ! (int)$line |
123 |
&& 2 == sscanf($line, "%c %d", &$ctrl, &$num) |
124 |
&& 'W' == $ctrl |
125 |
) { |
126 |
echo "W $num\n"; |
127 |
$pos = ftell($this->mst); |
128 |
$mfn = $num; |
129 |
fseek($this->xrf, 8*$mfn); |
130 |
continue; |
131 |
} |
132 |
$lines++; |
133 |
} |
134 |
fflush( $this->xrf ); |
135 |
} // mkxref |
136 |
|
137 |
|
138 |
/** |
139 |
@return string number $mfn or null |
140 |
*/ |
141 |
function read ( $mfn ) |
142 |
{ |
143 |
fseek($this->xrf, 8*$mfn); |
144 |
$xrf = fread( $this->xrf, 8 ); |
145 |
if ( 8 != strlen($xrf) ) { |
146 |
// echo "no xrf for $mfn\n"; |
147 |
return null; |
148 |
} |
149 |
$xrf = unpack('Vpos/Vlen', $xrf); |
150 |
// echo "mfn $mfn pos ${xrf['pos']} len ${xrf['len']}\n"; |
151 |
if ( !$xrf['pos'] || !$xrf['len'] ) |
152 |
return null; |
153 |
fseek($this->mst, $xrf['pos']); |
154 |
$rec = fread($this->mst, $xrf['len']); |
155 |
return $xrf['len'] == strlen($rec) ? $rec : null; |
156 |
} |
157 |
|
158 |
|
159 |
/** |
160 |
write $data as $mfn or ++$this->len |
161 |
@return mfn |
162 |
*/ |
163 |
function write ( $data, $mfn = 0 ) |
164 |
{ |
165 |
if ( !$this->w ) |
166 |
trigger_error( $this->name.' not writable', E_USER_ERROR ); |
167 |
if ( !$mfn ) |
168 |
$mfn = ++$this->len; |
169 |
elseif ($this->len < $mfn) |
170 |
$this->len = $mfn; |
171 |
if ( !is_string($data) ) |
172 |
$len = 0; |
173 |
elseif ( $len = strlen($data) ) { |
174 |
if ("\n" == $data{$len-1}) |
175 |
$len--; |
176 |
else |
177 |
$data .= "\n"; |
178 |
} |
179 |
fseek($this->mst, 0, SEEK_END); // prbly not needed by mode a+ |
180 |
fwrite($this->mst, "\nW\t$mfn\n"); |
181 |
fflush($this->mst); |
182 |
$pos = ftell($this->mst); |
183 |
if ( $len ) |
184 |
fwrite($this->mst, $data); |
185 |
fseek($this->xrf, 8*$mfn); |
186 |
// echo "write $mfn len $len\n"; |
187 |
fwrite($this->xrf, pack('VV', $pos, $len)); |
188 |
fflush($this->xrf); |
189 |
return $mfn; |
190 |
} |
191 |
|
192 |
|
193 |
function compact () |
194 |
{ |
195 |
if ( !$this->w ) |
196 |
trigger_error( $this->name.' not writable', E_USER_ERROR ); |
197 |
$c = fopen($this->name.'.new', 'w+'); |
198 |
flock($c, LOCK_EX); // so we have exclusive locks on both |
199 |
for ( $n=1; $n<=$this->len; $n++ ) |
200 |
fwrite($c, is_null($data = $this->read($n)) |
201 |
? "\n" : "\n".$data."\n" |
202 |
); |
203 |
fflush($c); |
204 |
if ( file_exists($this->name.'.old') ) |
205 |
unlink($this->name.'.old'); |
206 |
rename($this->name.'.txt', $this->name.'.old'); |
207 |
rename($this->name.'.new', $this->name.'.txt'); |
208 |
fclose($this->mst); |
209 |
$this->mst = $c; |
210 |
$this->mkxref(); // frob the xref in place |
211 |
} |
212 |
|
213 |
} // class Isis_File |
214 |
?> |