Welcome, guest | Sign In | My Account | Store | Cart

The class presented bellow (Disk Abstraction Layer 1) is designed to provide a very easy interface to work with secondary memory where IO errors may occur and data is worked with in blocks. DAL1 allows access to a hard drive (via its driver) so that it can be accessed in a file-like way. IO errors are also taken care of at this level (which can cause problems at extremely high probabilities of failure.

Python, 229 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import disk

################################################################################

class DiskDriver(disk.DiskDriver):

    # Get Configuration Information
    def info(self):
        return self.__blocks, self.__size

################################################################################

class DAL1:

    # DEFAULTS (16 MB)
    BLOCKS = (2 ** 12)
    SIZE = (2 ** 12)

    # Disk Abstraction Layer
    def __init__(self):
        self.__disk = DiskDriver(self.BLOCKS, self.SIZE)
        self.__blocks, self.__size = self.__disk.info()
        self.__end = self.__blocks * self.__size

    # Read Some Data
    def read(self, index, size):
        assert type(index) is int and 0 <= index < self.__end
        assert type(size) is int and index < index + size <= self.__end
        first_index = index
        last_index = index + size - 1
        first_block = first_index / self.__size + 1
        last_block = last_index / self.__size + 2
        stream = ''.join([self.__read(block) for block in \
                          range(first_block, last_block)])
        first_index = index % self.__size
        last_index = first_index + size
        return stream[first_index:last_index]

    # Write Some Data
    def write(self, index, data):
        size = len(data)
        assert type(index) is int and 0 <= index < self.__end
        assert type(data) is str and index < index + size <= self.__end
        first_index = index
        last_index = index + size - 1
        first_block = first_index / self.__size + 1
        last_block = last_index / self.__size + 2
        blocks = last_block - first_block
        if blocks == 1:
            if size == self.__size:
                self.__write(first_block, data)
            else:
                stream = self.__read(first_block)
                first_index = index % self.__size
                last_index = first_index + size
                stream = stream[:first_index] + data + stream[last_index:]
                self.__write(first_block, stream)
        else:
            if index % self.__size:
                stream = self.__read(first_block)
                first_index = index % self.__size
                last_index = self.__size - first_index
                stream = stream[:first_index] + data[:last_index]
                data = data[last_index:]
                self.__write(first_block, stream)
            else:
                last_index = self.__size
                stream = data[:last_index]
                data = data[last_index:]
                self.__write(first_block, stream)
            first_block += 1
            last_block -= 1
            for block in range(first_block, last_block):
                self.__write(block, data[:self.__size])
                data = data[self.__size:]
            size = len(data)
            if size == self.__size:
                self.__write(last_block, data)
            else:
                stream = self.__read(last_block)
                stream = data + stream[size:]
                self.__write(last_block, stream)

    # Erase Some Data
    def erase(self, index, size):
        assert type(index) is int and 0 <= index < self.__end
        assert type(size) is int and index < index + size <= self.__end
        first_index = index
        last_index = index + size - 1
        first_block = first_index / self.__size + 1
        last_block = last_index / self.__size + 2
        blocks = last_block - first_block
        if blocks == 1:
            if size == self.__size:
                self.__erase(first_block)
            else:
                stream = self.__read(first_block)
                first_index = index % self.__size
                last_index = first_index + size
                stream = stream[:first_index] + chr(0) * size + \
                         stream[last_index:]
                self.__write(first_block, stream)
        else:
            if index % self.__size:
                stream = self.__read(first_block)
                first_index = index % self.__size
                last_index = self.__size - first_index
                stream = stream[:first_index] + chr(0) * last_index
                self.__write(first_block, stream)
            else:
                self.__erase(first_block)
            first_block += 1
            last_block -= 1
            for block in range(first_block, last_block):
                self.__erase(block)
            last_index = index + size - 1
            size = (last_index % self.__size) + 1
            if size == self.__size:
                self.__erase(last_block)
            else:
                stream = self.__read(last_block)
                stream = chr(0) * size + stream[size:]
                self.__write(last_block, stream)

    # Probability Of Failure
    def fail(self, probability):
        self.__disk.fail(probability)

    # Dump To File
    def dump(self, name):
        self.__disk.dump(name)

    # Load From File
    def load(self, name):
        self.__disk.load(name)
        self.__blocks, self.__size = self.__disk.info()
        self.__end = self.__blocks * self.__size
        self.BLOCKS, self.SIZE = self.__blocks, self.__size

    # Private Utility Function
    def __read(self, block):
        while True:
            try:
                return self.__disk.read(block)
            except:
                pass

    # Private Utility Function
    def __write(self, block, data):
        while True:
            try:
                return self.__disk.write(block, data)
            except:
                pass

    # Private Utility Function
    def __erase(self, block):
        while True:
            try:
                return self.__disk.erase(block)
            except:
                pass

################################################################################

class Test:

    # DAL1 Test Class
    def __init__(self, size):
        self.__string = chr(0) * size

    # Read Some Data
    def read(self, index, size):
        assert type(index) is int and 0 <= index < len(self)
        assert type(size) is int and index < index + size <= len(self)
        return self.__string[index:index+size]

    # Write Some Data
    def write(self, index, data):
        assert type(index) is int and 0 <= index < len(self)
        assert type(data) is str and index < index + len(data) <= len(self)
        self.__string = self.__string[:index] + data + \
                        self.__string[index+len(data):]

    # Erase Some Data
    def erase(self, index, size):
        self.write(index, chr(0) * size)

    # Get Length Information
    def __len__(self):
        return len(self.__string)

################################################################################

def test():
    from os import urandom
    from random import randint
    BLOCKS = DAL1.BLOCKS = 100
    SIZE = DAL1.SIZE = 100
    test1 = DAL1()
    test2 = Test(BLOCKS * SIZE)
    test1.fail(0.999)
    for temp in range(BLOCKS / 2):
        print 'Write:', temp + 1, '/', BLOCKS / 2
        write = randint(0, (BLOCKS * SIZE) / 2), urandom(randint(1, SIZE * 2))
        test1.write(*write)
        test2.write(*write)
        index = randint((BLOCKS * SIZE) / 2, BLOCKS * SIZE)
        data = urandom(randint(1, SIZE * 2))
        write = index - len(data) + 1, data
        test1.write(*write)
        test2.write(*write)
    for temp in range(BLOCKS / 20):
        print 'Erase:', temp + 1, '/', BLOCKS / 20
        erase = randint(0, (BLOCKS * SIZE) / 2), randint(1, SIZE * 2)
        test1.erase(*erase)
        test2.erase(*erase)
        index = randint((BLOCKS * SIZE) / 2, BLOCKS * SIZE)
        size = randint(1, SIZE * 2)
        erase = index - size + 1, size
        test1.erase(*erase)
        test2.erase(*erase)
    raw_input('Tested To ' + str(test1.read(0, BLOCKS * SIZE) == \
                                 test2.read(0, BLOCKS * SIZE)))

################################################################################

if __name__ == '__main__':
    test()

This program is part of a series of files with the purpose of implementing a file system from scratch. disk.py can be found at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/492208