Class: PEROBS::FixedSizeBlobFile

Inherits:
Object
  • Object
show all
Defined in:
lib/perobs/FixedSizeBlobFile.rb

Overview

This class implements persistent storage space for fixed size data blobs. The blobs can be stored and retrieved and can be deleted again. The FixedSizeBlobFile manages the storage of the blobs and free storage spaces. The files grows and shrinks as needed. A blob is referenced by its address.

Instance Method Summary collapse

Constructor Details

#initialize(dir, name, entry_bytes) ⇒ FixedSizeBlobFile

Create a new stack file in the given directory with the given file name.

Parameters:

  • dir (String)

    Directory

  • name (String)

    File name

  • entry_bytes (Fixnum)

    Number of bytes each entry must have



44
45
46
47
48
49
# File 'lib/perobs/FixedSizeBlobFile.rb', line 44

def initialize(dir, name, entry_bytes)
  @file_name = File.join(dir, name + '.blobs')
  @entry_bytes = entry_bytes
  @free_list = StackFile.new(dir, name + '-freelist', 8)
  @f = nil
end

Instance Method Details

#clearObject

Delete all data.



88
89
90
91
92
# File 'lib/perobs/FixedSizeBlobFile.rb', line 88

def clear
  @f.truncate(0)
  @f.flush
  @free_list.clear
end

#closeObject

Close the blob file. This method must be called before the program is terminated to avoid data loss.



67
68
69
70
71
72
73
74
75
# File 'lib/perobs/FixedSizeBlobFile.rb', line 67

def close
  @free_list.close
  begin
    @f.flush
    @f.close
  rescue IOError => e
    PEROBS.log.fatal "Cannot close blob file #{@file_name}: #{e.message}"
  end
end

#delete_blob(address) ⇒ Object

Delete the blob at the given address.

Parameters:

  • address (Fixnum)

    Address of blob to delete



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/perobs/FixedSizeBlobFile.rb', line 158

def delete_blob(address)
  begin
    @f.seek(address_to_offset(address))
    if (@f.read(1).unpack('C')[0] != 1)
      PEROBS.log.fatal "There is no blob stored at address #{address}"
    end
    @f.seek(address_to_offset(address))
    @f.write([ 0 ].pack('C'))
  rescue IOError => e
    PEROBS.log.fatal "Cannot delete blob at address #{address}: " +
      e.message
  end
  # Add the address to the free list.
  @free_list.push([ address ].pack('Q'))
end

#empty?Boolean

Returns:

  • (Boolean)


94
95
96
97
# File 'lib/perobs/FixedSizeBlobFile.rb', line 94

def empty?
  sync
  @f.size == 0
end

#free_addressFixnum

Return the address of a free blob storage space. Addresses start at 0 and increase linearly.

Returns:

  • (Fixnum)

    address of a free blob space



102
103
104
105
106
107
108
109
110
111
# File 'lib/perobs/FixedSizeBlobFile.rb', line 102

def free_address
  if (bytes = @free_list.pop)
    # Return an entry from the free list.
    return bytes.unpack('Q')[0]
  else
    # There is currently no free entry. Return the address at the end of
    # the file.
    offset_to_address(@f.size)
  end
end

#openObject

Open the blob file.



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/perobs/FixedSizeBlobFile.rb', line 52

def open
  begin
    if File.exist?(@file_name)
      @f = File.open(@file_name, 'rb+')
    else
      @f = File.open(@file_name, 'wb+')
    end
  rescue IOError => e
    PEROBS.log.fatal "Cannot open blob file #{@file_name}: #{e.message}"
  end
  @free_list.open
end

#retrieve_blob(address) ⇒ String

Retrieve a blob from the given address.

Parameters:

  • address (Fixnum)

    Address to store the blob

Returns:

  • (String)

    blob bytes



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/perobs/FixedSizeBlobFile.rb', line 137

def retrieve_blob(address)
  begin
    if (offset = address_to_offset(address)) >= @f.size
      return nil
    end

    @f.seek(address_to_offset(address))
    if (@f.read(1).unpack('C')[0] != 1)
      return nil
    end
    bytes = @f.read(@entry_bytes)
  rescue IOError => e
    PEROBS.log.fatal "Cannot retrieve blob at adress #{address}: " +
      e.message
  end

  bytes
end

#store_blob(address, bytes) ⇒ Object

Store the given byte blob at the specified address. If the blob space is already in use the content will be overwritten.

Parameters:

  • address (Fixnum)

    Address to store the blob

  • bytes (String)

    bytes to store



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/perobs/FixedSizeBlobFile.rb', line 117

def store_blob(address, bytes)
  if bytes.length != @entry_bytes
    PEROBS.log.fatal "All stack entries must be #{@entry_bytes} " +
      "long. This entry is #{bytes.length} bytes long."
  end
  begin
    @f.seek(address_to_offset(address))
    # The first byte is tha flag byte. It's set to 1 for cells that hold a
    # blob. 0 for empty cells.
    @f.write([ 1 ].pack('C'))
    @f.write(bytes)
    @f.flush
  rescue IOError => e
    PEROBS.log.fatal "Cannot store blob at address #{address}: #{e.message}"
  end
end

#syncObject

Flush out all unwritten data.



78
79
80
81
82
83
84
85
# File 'lib/perobs/FixedSizeBlobFile.rb', line 78

def sync
  @free_list.sync
  begin
    @f.sync
  rescue IOError => e
    PEROBS.log.fatal "Cannot sync blob file #{@file_name}: #{e.message}"
  end
end