lib/perobs/FlatFileBlobHeader.rb in perobs-2.5.0 vs lib/perobs/FlatFileBlobHeader.rb in perobs-3.0.0

- old
+ new

@@ -29,55 +29,72 @@ module PEROBS # The FlatFile blob header has the following structure: # - # 1 Byte: Mark byte. + # 1 Byte: Flags byte. # Bit 0: 0 deleted entry, 1 valid entry # Bit 1: 0 unmarked, 1 marked # Bit 2: 0 uncompressed data, 1 compressed data - # Bit 3 - 7: reserved, must be 0 + # Bit 3: 0 current entry, 1 outdated entry + # Bit 4 - 7: reserved, must be 0 # 8 bytes: Length of the data blob in bytes # 8 bytes: ID of the value in the data blob # 4 bytes: CRC32 checksum of the data blob # - # If the bit 0 of the mark byte is 0, only the length is valid. The blob is + # If the bit 0 of the flags byte is 0, only the length is valid. The blob is # empty. Only of bit 0 is set then entry is valid. class FlatFileBlobHeader # The 'pack()' format of the header. FORMAT = 'CQQL' # The length of the header in bytes. LENGTH = 21 + VALID_FLAG_BIT = 0 + MARK_FLAG_BIT = 1 + COMPRESSED_FLAG_BIT = 2 + OUTDATED_FLAG_BIT = 3 - attr_reader :mark, :length, :id, :crc + attr_reader :addr, :flags, :length, :id, :crc - # Create a new FlatFileBlobHeader with the given mark, length, id and crc. - # @param mark [Fixnum] 8 bit number, see above + # Create a new FlatFileBlobHeader with the given flags, length, id and crc. + # @param file [File] the FlatFile that contains the header + # @param addr [Integer] the offset address of the header in the file + # @param flags [Fixnum] 8 bit number, see above # @param length [Fixnum] length of the header in bytes # @param id [Integer] ID of the blob entry # @param crc [Fixnum] CRC32 checksum of the blob entry - def initialize(mark, length, id, crc) - @mark = mark + def initialize(file, addr, flags, length, id, crc) + @file = file + @addr = addr + @flags = flags @length = length @id = id @crc = crc end # Read the header from the given File. # @param file [File] # @return FlatFileBlobHeader def FlatFileBlobHeader::read(file) begin + addr = file.pos buf = file.read(LENGTH) rescue IOError => e - PEROBS.log.fatal "Cannot read blob header in flat file DB: #{e.message}" + PEROBS.log.error "Cannot read blob header in flat file DB: #{e.message}" + return nil end return nil unless buf - FlatFileBlobHeader.new(*buf.unpack(FORMAT)) + if buf.length != LENGTH + PEROBS.log.error "Incomplete FlatFileBlobHeader: Only #{buf.length} " + + "bytes of #{LENGTH} could be read" + return nil + end + + FlatFileBlobHeader.new(file, addr, *buf.unpack(FORMAT)) end # Read the header from the given File. # @param file [File] # @param addr [Integer] address in the file to start reading @@ -94,49 +111,107 @@ if buf.nil? || buf.length != LENGTH PEROBS.log.fatal "Cannot read blob header " + "#{id ? "for ID #{id} " : ''}at address " + "#{addr}" end - header = FlatFileBlobHeader.new(*buf.unpack(FORMAT)) + header = FlatFileBlobHeader.new(file, addr, *buf.unpack(FORMAT)) if id && header.id != id PEROBS.log.fatal "Mismatch between FlatFile index and blob file " + "found for entry with ID #{id}/#{header.id}" end return header end # Write the header to a given File. # @param file [File] - def write(file) + def write begin - file.write([ @mark, @length, @id, @crc].pack(FORMAT)) + @file.seek(@addr) + @file.write([ @flags, @length, @id, @crc].pack(FORMAT)) rescue IOError => e PEROBS.log.fatal "Cannot write blob header into flat file DB: " + e.message end end + # Reset all the flags bit to 0. This marks the blob as invalid. + # @param file [File] The file handle of the blob file. + # @param addr [Integer] The address of the header + def clear_flags + begin + @file.seek(@addr) + @file.write([ 0 ].pack('C')) + @file.flush + rescue IOError => e + PEROBS.log.fatal "Clearing flags of FlatFileBlobHeader with ID " + + "#{@id} failed: #{e.message}" + end + end + # Return true if the header is for a non-empty blob. def is_valid? - bit_set?(0) + bit_set?(VALID_FLAG_BIT) end # Return true if the blob has been marked. def is_marked? - bit_set?(1) + bit_set?(MARK_FLAG_BIT) end + # Set the mark bit. + def set_mark_flag + set_flag(MARK_FLAG_BIT) + write_flags + end + + # Clear the mark bit. + def clear_mark_flag + clear_flag(MARK_FLAG_BIT) + write_flags + end + # Return true if the blob contains compressed data. def is_compressed? - bit_set?(2) + bit_set?(COMPRESSED_FLAG_BIT) end + # Set the outdated bit. The entry will be invalid as soon as the current + # transaction has been completed. + def set_outdated_flag + set_flag(OUTDATED_FLAG_BIT) + write_flags + end + + # Return true if the blob contains outdated data. + def is_outdated? + bit_set?(OUTDATED_FLAG_BIT) + end + private + def write_flags + begin + @file.seek(@addr) + @file.write([ @flags ].pack('C')) + @file.flush + rescue IOError => e + PEROBS.log.fatal "Writing flags of FlatFileBlobHeader with ID #{@id} " + + "failed: #{e.message}" + end + end + def bit_set?(n) mask = 1 << n - @mark & mask == mask + @flags & mask == mask + end + + def set_flag(n) + @flags |= (1 << n) + end + + def clear_flag(n) + @flags &= ~(1 << n) & 0xFF end end end