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 from array import array 23 import zlib 24 25 # Constants. 26 27 class File: 28 29 "A basic file abstraction." 30 31 def __init__(self, f): 32 self.f = f 33 self.reset() 34 35 def reset(self): 36 37 "To be used to reset the state of the reader or writer between records." 38 39 pass 40 41 def rewind(self): 42 self.f.seek(0) 43 self.reset() 44 45 def close(self): 46 if self.f is not None: 47 self.f.close() 48 self.f = None 49 50 class FileWriter(File): 51 52 "Writing basic data types to files." 53 54 def __init__(self, f): 55 File.__init__(self, f) 56 57 def write_number(self, number): 58 59 "Write 'number' to the file using a variable length encoding." 60 61 self.f.write(vint(number)) 62 63 def write_string(self, s, compress=0): 64 65 """ 66 Write 's' to the file, recording its length and compressing the string 67 if 'compress' is set to a true value. 68 """ 69 70 # Convert Unicode objects to strings. 71 72 if isinstance(s, unicode): 73 s = s.encode("utf-8") 74 75 # Compress the string if requested. 76 77 if compress: 78 cs = zlib.compress(s) 79 80 # Take any shorter than the original. 81 82 if len(cs) < len(s): 83 flag = "z" 84 s = cs 85 else: 86 flag = "-" 87 88 else: 89 flag = "" 90 91 # Write the length of the data before the data itself. 92 93 length = len(s) 94 self.f.write("".join([flag, vint(length), s])) 95 96 class FileReader(File): 97 98 "Reading basic data types from files." 99 100 def __init__(self, f): 101 File.__init__(self, f) 102 103 def read_number(self): 104 105 "Read a number from the file." 106 107 # Read each byte, adding it to the number. 108 109 a = array('B') 110 fromfile = a.fromfile 111 f = self.f 112 113 fromfile(f, 1) 114 csd = a[-1] 115 if csd < 128: 116 return csd 117 else: 118 while csd & 128: 119 fromfile(f, 1) 120 csd = a[-1] 121 return sum([((csd & 127) << (number * 7)) for (number, csd) in enumerate(a)]) 122 123 def read_string(self, decompress=0): 124 125 """ 126 Read a string from the file, decompressing the stored data if 127 'decompress' is set to a true value. 128 """ 129 130 read = self.f.read 131 132 # Decompress the data if requested. 133 134 if decompress: 135 flag = read(1) 136 else: 137 flag = "-" 138 139 length = self.read_number() 140 s = read(length) 141 142 # Perform decompression if applicable. 143 144 if flag == "z": 145 s = zlib.decompress(s) 146 147 # Convert strings to Unicode objects. 148 149 return unicode(s, "utf-8") 150 151 class FileOpener: 152 153 "Opening files using their filenames." 154 155 def __init__(self, filename): 156 self.filename = filename 157 158 def open(self, mode): 159 return open(self.filename, mode) 160 161 def close(self): 162 pass 163 164 # vim: tabstop=4 expandtab shiftwidth=4