diff --git a/lib/pstore.rb b/lib/pstore.rb index d717820..5e33035 100644 --- a/lib/pstore.rb +++ b/lib/pstore.rb @@ -126,6 +126,7 @@ class PStore @filename = file @abort = false @ultra_safe = false + @modified = false @thread_safe = thread_safe @lock = Mutex.new end @@ -141,6 +142,7 @@ class PStore def in_transaction_wr in_transaction raise PStore::Error, "in read-only transaction" if @rdonly + @modified = true end private :in_transaction, :in_transaction_wr @@ -313,17 +315,18 @@ class PStore @lock.synchronize do @rdonly = read_only @abort = false + @modified = false file = open_and_lock_file(@filename, read_only) if file begin - @table, checksum, original_data_size = load_data(file, read_only) + @table = load_data(file) catch(:pstore_abort_transaction) do value = yield(self) end if !@abort && !read_only - save_data(checksum, original_data_size, file) + save_data(file) end ensure file.close if !file.closed? @@ -342,7 +345,7 @@ class PStore end private - # Constant for relieving Ruby's garbage collector. + # Constants for compatibility. EMPTY_STRING = "" EMPTY_MARSHAL_DATA = Marshal.dump({}) EMPTY_MARSHAL_CHECKSUM = Digest::MD5.digest(EMPTY_MARSHAL_DATA) @@ -378,35 +381,16 @@ class PStore end # Load the given PStore file. - # If +read_only+ is true, the unmarshalled Hash will be returned. - # If +read_only+ is false, a 3-tuple will be returned: the unmarshalled - # Hash, an MD5 checksum of the data, and the size of the data. - def load_data(file, read_only) - if read_only - begin - table = load(file) - raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash) - rescue EOFError - # This seems to be a newly-created file. - table = {} - end - table - else - data = file.read - if data.empty? - # This seems to be a newly-created file. - table = {} - checksum = empty_marshal_checksum - size = empty_marshal_data.bytesize - else - table = load(data) - checksum = Digest::MD5.digest(data) - size = data.bytesize - raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash) - end - data.replace(EMPTY_STRING) - [table, checksum, size] + # The unmarshalled Hash will be returned. + def load_data(file) + begin + table = load(file) + raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash) + rescue EOFError + # This seems to be a newly-created file. + table = {} end + table end def on_windows? @@ -417,10 +401,10 @@ class PStore is_windows end - def save_data(original_checksum, original_file_size, file) + def save_data(file) new_data = dump(@table) - if new_data.bytesize != original_file_size || Digest::MD5.digest(new_data) != original_checksum + if @modified if @ultra_safe && !on_windows? # Windows doesn't support atomic file renames. save_data_with_atomic_file_rename_strategy(new_data, file) @@ -429,7 +413,7 @@ class PStore end end - new_data.replace(EMPTY_STRING) + new_data.clear end def save_data_with_atomic_file_rename_strategy(data, file)