1 #!/usr/bin/env python 2 3 """ 4 Generic file access. 5 6 Copyright (C) 2009 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License along 18 with this program. If not, see <http://www.gnu.org/licenses/>. 19 """ 20 21 from iixr.data import vint 22 import zlib 23 24 # Constants. 25 26 class File: 27 28 "A basic file abstraction." 29 30 def __init__(self, f): 31 self.f = f 32 self.reset() 33 34 def reset(self): 35 36 "To be used to reset the state of the reader or writer between records." 37 38 pass 39 40 def rewind(self): 41 self.seek(0) 42 self.reset() 43 44 def seek(self, offset): 45 46 "To be defined by readers." 47 48 pass 49 50 def flush(self): 51 52 "To be defined by writers." 53 54 pass 55 56 def close(self): 57 if self.f is not None: 58 self.flush() 59 self.f.close() 60 self.f = None 61 62 class FileWriter(File): 63 64 "Writing basic data types to files." 65 66 def __init__(self, f): 67 File.__init__(self, f) 68 69 def write_number(self, number): 70 71 "Write 'number' to the file using a variable length encoding." 72 73 self.write(vint(number)) 74 75 def write_string(self, s, compress=0): 76 77 """ 78 Write 's' to the file, recording its length and compressing the string 79 if 'compress' is set to a true value. 80 """ 81 82 # Convert Unicode objects to strings. 83 84 if isinstance(s, unicode): 85 s = s.encode("utf-8") 86 87 # Compress the string if requested. 88 89 if compress: 90 cs = zlib.compress(s) 91 92 # Take any shorter than the original. 93 94 if len(cs) < len(s): 95 flag = "z" 96 s = cs 97 else: 98 flag = "-" 99 100 else: 101 flag = "" 102 103 # Write the length of the data before the data itself. 104 105 length = len(s) 106 self.write(flag + vint(length) + s) 107 108 # Cache-affected methods. 109 110 def write(self, s): 111 self.f.write(s) 112 113 def tell(self): 114 return self.f.tell() 115 116 class FileReader(File): 117 118 "Reading basic data types from files." 119 120 def __init__(self, f): 121 File.__init__(self, f) 122 123 def read_number(self): 124 125 "Read a number from the file." 126 127 # Read each byte, adding it to the number. 128 129 shift = 0 130 number = 0 131 read = self.read 132 133 try: 134 csd = ord(read(1)) 135 while csd & 128: 136 number += ((csd & 127) << shift) 137 shift += 7 138 csd = ord(read(1)) 139 else: 140 number += (csd << shift) 141 except TypeError: 142 raise EOFError 143 144 return number 145 146 def read_string(self, decompress=0): 147 148 """ 149 Read a string from the file, decompressing the stored data if 150 'decompress' is set to a true value. 151 """ 152 153 # Decompress the data if requested. 154 155 if decompress: 156 flag = self.read(1) 157 else: 158 flag = "-" 159 160 length = self.read_number() 161 s = self.read(length) 162 163 # Perform decompression if applicable. 164 165 if flag == "z": 166 s = zlib.decompress(s) 167 168 # Convert strings to Unicode objects. 169 170 return unicode(s, "utf-8") 171 172 # Cache-affected methods. 173 174 def read(self, n): 175 return self.f.read(n) 176 177 def tell(self): 178 return self.f.tell() 179 180 def seek(self, offset): 181 self.f.seek(offset) 182 183 class FileOpener: 184 185 "Opening files using their filenames." 186 187 def __init__(self, filename): 188 self.filename = filename 189 190 def open(self, mode): 191 return open(self.filename, mode) 192 193 def close(self): 194 pass 195 196 # vim: tabstop=4 expandtab shiftwidth=4