source: tol/tolsh/tolsh.vfs/lib/tcllib1.10/tie/tie_file.tcl @ 8597867d

1941-docker-image-to-testdevel
Last change on this file since 8597867d was 8597867d, checked in by Jorge Pérez <josp.jorge@…>, 16 years ago

closes #976, se incluye tcllib1.10, base64 debe eliminarse del raiz de lib ya que esta incluido en tcllib

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@2556 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 6.5 KB
Line 
1# tie_file.tcl --
2#
3#       Data source: Files.
4#
5# Copyright (c) 2004 Andreas Kupries <andreas_kupries@users.sourceforge.net>
6#
7# See the file "license.terms" for information on usage and redistribution
8# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
9#
10# RCS: @(#) $Id: tie_file.tcl,v 1.8 2006/09/19 23:36:18 andreas_kupries Exp $
11
12# ### ### ### ######### ######### #########
13## Requisites
14
15package require snit
16package require tie
17
18# ### ### ### ######### ######### #########
19## Implementation
20
21snit::type ::tie::std::file {
22    # ### ### ### ######### ######### #########
23    ## Notes
24
25    ## This data source maintains an internal cache for higher
26    ## efficiency, i.e. to avoid having to go out to the slow file.
27
28    ## This cache is handled as follows
29    ##
30    ## - All write operations invalidate the cache and write directly
31    ##   to the file.
32    ##
33    ## - All read operations load from the file if the cache is
34    ##   invalid, and from the cache otherwise
35
36    ## This scheme works well in the following situations:
37
38    ## (a) The data source is created, and then only read from.
39    ## (b) The data source is created, and then only written to.
40    ## (c) The data source is created, read once, and then only
41    ##     written to.
42
43    ## This scheme works badly if the data source is opened and then
44    ## randomly read from and written to. The cache is useless, as it
45    ## is continuously invalidated and reloaded.
46
47    ## This no problem from this developers POV of view however.
48    ## Consider the context. If you have this situation just tie the
49    ## DS to an array A after creation. The tie framework operates on
50    ## the DS in mode (c) and A becomes an explicit cache for the DS
51    ## which is not invalidated by writing to it. IOW this covers
52    ## exactly the situation the DS by itself is not working well for.
53
54    # ### ### ### ######### ######### #########
55    ## Specials
56
57    pragma -hastypemethods no
58    pragma -hasinfo        no
59    pragma -simpledispatch yes
60
61    # ### ### ### ######### ######### #########
62    ## API : Construction & Destruction
63
64    constructor {thepath} {
65        # Locate and open the journal file.
66
67        set path [::file normalize $thepath]
68        if {[::file exists $path]} {
69            set chan [open $path {RDWR EXCL APPEND}]
70        } else {
71            set chan [open $path {RDWR EXCL CREAT APPEND}]
72        }
73        fconfigure $chan -buffering none -encoding utf-8
74        return
75    }
76
77    destructor {
78        # Release the channel to the journal file, should it be open.
79        if {$chan ne ""} {close $chan}
80        return
81    }
82
83    # ### ### ### ######### ######### #########
84    ## API : Data source methods
85
86    method get {} {
87        if {![::file size $path]} {return {}}
88        $self LoadJournal
89        return [array get cache]
90    }
91
92    method set {dict} {
93        puts $chan [list array set $dict]
94        $self Invalidate
95        return
96    }
97
98    method unset {{pattern *}} {
99        puts $chan [list array unset $pattern]
100        $self Invalidate
101        return
102    }
103
104    method names {} {
105        if {![::file size $path]} {return {}}
106        $self LoadJournal
107        return [array names cache]
108    }
109
110    method size {} {
111        if {![::file size $path]} {return 0}
112        $self LoadJournal
113        return [array size cache]
114    }
115
116    method getv {index} {
117        if {![::file size $path]} {
118            return -code error "can't read \"$index\": no such variable"
119        }
120        $self LoadJournal
121        return $cache($index)
122    }
123
124    method setv {index value} {
125        puts $chan [list set $index $value]
126        $self Invalidate
127        return
128    }
129
130    method unsetv {index} {
131        puts $chan [list unset $index]
132        $self Invalidate
133        return
134    }
135
136    # ### ### ### ######### ######### #########
137    ## Internal : Instance data
138
139    variable chan {} ; # Channel to write the journal.
140    variable path {} ; # Path to journal file.
141
142    # Journal loading, and cache.
143
144    variable count 0         ; # #Operations in the journal.
145    variable cvalid 0        ; # Validity of the cache.
146    variable cache -array {} ; # Cache for journal
147
148    # Management of the cache: See notes at beginning.
149
150    # ### ### ### ######### ######### #########
151    ## Internal: Loading from the journal.
152
153    method LoadJournal {} {
154        if {$cvalid} return
155        $self Replay
156        $self Compact
157        return
158    }
159
160    method Replay {} {
161        # Use a safe interp for the evaluation of the journal file.
162        # (Empty safe for the hidden commands and the aliases we insert).
163
164        # Called for !cvalid, implies cache does not exist
165
166        set ip [interp create -safe]
167        foreach c [$ip eval {info commands}] {
168            if {$c eq "rename"} continue
169            $ip eval [list rename $c {}]
170        }
171        $ip eval {rename rename {}}
172
173        interp alias $ip set   {} $self Set
174        interp alias $ip unset {} $self Unset
175        interp alias $ip array {} $self Array
176
177        array set cache {}
178        set       count 0
179
180        $ip invokehidden -global source $path
181        interp delete $ip
182
183        set cvalid 1
184        return
185    }
186
187    method Compact {} {
188        # Compact the journal
189
190        #puts @@/2*$count/3*[array size temp]/=/[expr {2*$count >= 3*[array size temp]}]
191
192        # ASSERT cvalid
193
194        # do not compact <=>
195        # 2*ops < 3*size <=>
196        # ops < 3/2*size <=>
197        # ops < 1.5*size
198
199        if {(2*$count) < (3*[array size cache])} return
200
201        ::file delete -force ${path}.new
202        set new [open ${path}.new {RDWR EXCL CREAT APPEND}]
203        fconfigure $new -buffering none -encoding utf-8
204
205        # Compress current contents into a single multi-key load operation.
206        puts $new [list array set [array get cache]]
207
208        if {$::tcl_platform(platform) eq "windows"} {
209            # For windows the open channels prevent us from
210            # overwriting the old file. We have to leave
211            # attackers a (small) window of opportunity for
212            # replacing the file with something they own :(
213            close $chan
214            close $new
215            ::file rename -force ${path}.new $path
216            set chan [open ${path} {RDWR EXCL APPEND}]
217            fconfigure $chan -buffering none -encoding utf-8
218        } else {
219            # Copy compacted journal over the existing one.
220            ::file rename -force ${path}.new $path
221            close $chan
222            set    chan $new
223        }
224        return
225    }
226
227    method Set {index value} {
228        set cache($index) $value
229        incr count
230        return
231    }
232
233    method Unset {index} {
234        unset cache($index)
235        incr count
236        return
237    }
238
239    method Array {cmd detail} {
240        # syntax : set   dict
241        # ...... : unset pattern
242
243        if {$cmd eq "set"} {
244            array set cache $detail
245        } elseif {$cmd eq "unset"} {
246            array unset cache $detail
247        } else {
248            return -code error "Illegal command \"$cmd\""
249        }
250        incr count
251        return
252    }
253
254    method Invalidate {} {
255        if {!$cvalid} return
256        set cvalid 0
257        unset cache
258        return
259    }
260
261    # ### ### ### ######### ######### #########
262}
263
264# ### ### ### ######### ######### #########
265## Ready to go
266
267::tie::register ::tie::std::file as file
268package provide   tie::std::file 1.0.2
Note: See TracBrowser for help on using the repository browser.