Two two client sources analysis: Storage class
Author: Ma Ying-jeou
Date: 2004-6-28
Since the Storage class is relatively simple, I will not comment on the source code basis. Master Storage and lay the foundation for further analysis of the StorageWrapper class.
Some explanations:
1. The Storage class encapsulates the read and write operations for disk files.
2, BT supports downloads of a single file, and also supports multiple files, including available subdirectory. However, it is not downloaded and uploaded by documents, but in units of "file pieces". This can be seen in the BT protocol specification and another article in BT technology. Therefore, for multiple files, it is also treated as a "big file" that spliced. For example, there is files AAA and BBB, the size is 400 and 1000, respectively, and it is considered a large file with a size of 1400, and in order to make a break division.
3, the file is in the download process, but also provides upload, so WB and RB all the read and write methods are opened, WB and RB all. After downloading, change it to a read-only way.
4. Since the download may be interrupted, some data on the disk may already exist on the disk when Storage is initialized, and the size of the file must be checked. In order to facilitate the description, we refer to the size of the full file as "actual length", and the current size of the file is "current length".
Class Storage:
# Files is a list of binary groups (LIST), and the binary group contains file names and lengths, for example:
[("AAA", 100), ("BBB", 200)]
DEF __INIT __ (Self, Files, Open, EXISTS, GETSIZE):
Self.ranges = []
# Note, here is 0L, the Laid L indication is long shaping, not 01.
Total = 0L
SO_FAR = 0L
For File, Length in Files:
if Length! = 0:
# Ranges is a three-way list, the format of the third rating is: at the beginning of the "entire" file, end position, file name. BT is handling multiple files as a big file that splices.
Self.ranges.Append (Total, Total Length, File)
Total = Length
# SO_FAR is the total length of the actual file, it seems to have no role
IF exissrs:
l = getsize (file)
IF l> Length:
L = Length
SO_FAR = L
# If the file length is 0, create an empty file
Elif Not Exists (file):
Open (file, 'wb'). Close ()
# Begins is a list to save the starting position of each file
Self.begins = [i [0] for i in self.ranges]
Self.total_Length = Total
Self.handles = {}
Self.whandles = {}
SELF.TOPS = {}
# For each file,
For File, Length in Files:
# If the file already exists
IF exissrs:
l = getsize (file)
# If the file length is inconsistent, the description has not been downloaded completely, and the file is opened in the way reading and writing (RB ). IF L! = Length:
Handles is a dictionary that saves all handles that are opened (whether only read or read and write)
WHANDLES is a dictionary that records if the corresponding file is written to open (read and write is also a write).
Self.handles [file] = open (file, 'RB ')
Self.wHandles [file] = 1 (here is the number 1, not letters L)
# If the file length is greater than the actual length, it should be wrong, cut off.
IF l> Length:
Self.handles [file] .truncate (Length)
If the file length is consistent with the actual length, then download has been completed, open in a read-only mode.
Else:
Self.handles [file] = open (file, 'rb')
# TOPS is a dictionary that saves the "current length" of the corresponding file.
Self.tops [file] = L (here is letter L, not numbers 1)
# If the file does not exist, then open in a way to read (W )
Else:
Self.handles [file] = open (file, 'wb ')
Self.whandles [file] = 1
# 起断 The starting position is POS, the length of the file is the file, and it exists on the disk before the Storage initialization. This function will be mentioned after analyzing the STOAGEWRAPPER class.
If you already exist, then returns true, otherwise false.
Note: If the part of this piece of pieces already exists on the disk, then false is returned.
When analyzing the StorageWrapper, it was found that the analysis is not correct. This function means:
It is determined that the starting position is POS, the length of the length of Length, has been assigned a space on the disk before the Storage initialization.
For example, the size of 1024K, if you get the first piece (from 256K to 512K), then this time, the size of the file on the disk is 512K (that is, 512K), although the 0th piece (from 0 to 0 256K) has not yet been obtained, but this "empty hole" will be retained on the disk.
DEF WAS_PREALLOCATED (Self, Pos, Length):
For File, Begin, End In self._intervals (POS, Length):
IF self.tops.get (file, 0) Return False Return True # Turn all the files that open to read and write, change to read-only mode Def set_readonly (Self): # May raise orerror or oserror For file in self.whandles.keys (): Old = self.handles [file] Old.flush () Old.close () Self.handles [file] = open (file, 'rb') # Get the total length of all files Def get_total_length (self): Return Self.Total_Length This function means to check where the starting position is POS, the size of the size is the actual location of the AMOUNT? For example, suppose there are two files, the AAA and BBB, the size is 400 and 1000, respectively, then the POS is 300, which file is the file piece of the Amount belongs to? It belongs to two files, so returns ("AAA", 300, 400), ("BBB", 0, 100)], That is, it includes both the data from 300 to 400 in the AAA file, and also contains the BBB file from 0 to 100 data. Def_Intervals (Self, Pos, Amount): r = [] # 帖子 is the end position of this piece. STOP = POS AMOUNT # With this function, you can first position in which file, pay attention, maybe in multiple files (if a file is too small, then a data may span several files) # In the example, it explains the following sentence, assuming Begins = [100, 200, 400, 1000], and POS = 250, then bisect_right (Self.Begins, POS) returns 2, and P = Bisect_Right (Self.Begins, POS) - 1 is 1, which means that the file "piece" of the starting position is 250, which is at least the first file (starting from 0), which is the file that is 200. P = bisect_right (Self.Begins, POS) - 1 # r is a list of a three-way group, the ternary group format is (file name, at the beginning of the file, at the end of the file). While P Begin, end, file = self.ranges [p] R.Append (File, Max (POS, Begin) - Begin, MIN (End, STOP) - BEGIN) P = 1 Return R # 把 Starting from POS, Amount long read from the file, convert into a string Def Read (Self, Pos, Amount): r = [] For File, POS, End in self._intervals: h = self.handles [file] H.seek (POS) R.Append (H. Read (end - poS)) # Convert LIST to a string Return '' .join (r) # Write a string to the corresponding disk file. Def Write (Self, Pos, s): # might raise an oreror Total = 0 For File, Begin, End in self._intervals (POS, LEN (s)): # If the file is not opened in writing, then it is changed to read and write if not self.whandles.has_key (file): Self.handles [file] .close () Self.handles [file] = open (file, 'RB ') Self.wHandles [file] = 1h = self.handles [file] # Via the SEEK function mobile file pointer, it can be seen that the file is not written in order, because the pieces obtained are random, so write is also random. # Here is a question, assuming that the second file is obtained, the start is 1000, the size is 500, and the first piece is not yet available, then the file pointer is to be moved to 1000, and write 500 bytes. At this time, the size of the file should be 1500, although 1000 bytes in front are "empty cave". Then, until the end, didn't get the first piece, and how to detect it? (By checking Total?) H.seek (Begin) H.write (s [Total: Total end - begin]) Total = End - Begin # Close all open files Def Close (Self): For h in self.handles.values (): H.close ()