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