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