Data::...eadonly(3pm)

πŸ’– NAME πŸ’–

Data::CompactReadonly β€” create and read Compact Read Only Databases πŸŽ€

(yeah it's a User Contributed Perl Documentation page, filed under Data::...eadonly(3pm) in the man system, don't @ me about the truncation, that's just how man pages abbreviate πŸ’…)


🌸 DESCRIPTION 🌸

Okay so bestie, picture a Compact Read Only Database that sips like almost no memory. That's the whole vibe. πŸ¦‹

The catch: once you've created it, you basically can't update it β€” like, not practically. The only move is to re-write the entire thing from scratch. That's not a bug, that's the deal. πŸ™ˆ

The goal? Random-access read performance on a par with DBM::Deep, but with files that are way smaller. Iconic tradeoff, honestly.


✨ VERSION 'NUMBERS' ✨

This module does semantic versioning. Which means the version "number" isn't really a number β€” it's three parts glued together: major.minor.patch. 🫢

  • The major number goes up when the API changes in a way that's incompatible β€” breaking change, periodt.
  • The minor number goes up when backward-compatible stuff gets added to the API.
  • The patch number goes up when bugs get fixed backward-compatibly.

πŸŽ€ FILE FORMAT VERSIONS πŸŽ€

Every version so far supports file format version 0 only. That's it, that's the list. 😌

Wanna know what that actually means? Go read Data::CompactReadonly::V0::Format, it's all in there.


πŸ’… METHODS πŸ’…

🌈 create 🌈

Takes two arguments:

  1. the name of the file you wanna write a database into
  2. some data

And the data can be literally anything: undef, a number, some text, or a reference to an array or hash β€” and those can contain undefs, numbers, text, more references to arrays or hashes, and so on ad infinitum. It's turtles all the way down, bestie. 🐒

⚠️ Heads up: this method may be very slow.

Why? Because it builds the file by doing a ton of little writes and seek()ing all over the place. It doesn't do anything clever to figure out what pointer size to use β€” it just tries the shortest first, and if that's not enough it tries again, and again, bigger each time. If you want the tea on pointer sizes, see Data::CompactReadonly::Format.

It may also eat lots of memory 😭. It keeps a cache of everything it has seen while building your database, so it can re-use data by just pointing at it instead of writing multiple copies of the same data into the file. Dedup queen behavior, but at a cost.

It tries really hard to preserve data types. So for example, 60000 is stored and read back as an integer, but "60000" is stored and read back as a string. ✨

This means you can correctly store and retrieve "007" β€” but 007 will have the leading zeroes removed before Data::CompactReadonly ever sees it, so it'll be treated as exactly equivalent to 7. Same energy for floating point values: "7.10" is stored as a four byte string, but 7.10 is stored the same as 7.1, as an eight byte IEEE754 double precision float. And note that perl parses values like 7.0 as floating point β€” and thus so does this module. 😌

Finally, while the file format permits numeric keys and Booleans in hashes, this method always coerces them to text. Why?

  • It does it to numbers because if you allow numeric keys, numbers that can't be represented in an int β€” like 1e100 or 3.14 β€” will be subject to floating point imprecision, so it's unlikely you'd ever be able to retrieve them, since no exact match is possible.
  • And it does it to Booleans because when you un-serialise them on an older perl they may get confused with strings, leading to loss of data if those strings are also present as keys in the dictionary.

πŸ¦‹ read πŸ¦‹

Takes a single compulsory argument β€” which is either a filename or an already open file handle β€” plus some options.

If the first argument is a filehandle, the current file pointer should be at the start of the database (not necessarily the start of the file; the database could be sitting inside a __DATA__ segment, cute right?) and it must have been opened in "just the bytes ma'am" mode. πŸ’…

🚨 It is a fatal error to pass in a filehandle that wasn't opened correctly, or the name of a file that can't be opened, or one that doesn't contain a valid database. No vibes-based error recovery here.

The options are name/value pairs. Valid options are:

  • tie β€” If true, return tied objects instead of normal objects. That means you'll be able to access data by de-referencing and pretending to access elements directly. Under the bonnet it wraps around the objects as documented below, so it's just a layer of indirection. On modern hardware you probably won't notice the concomitant slow down, but you may appreciate the convenience. 🫢
  • fast_collections β€” If true, Dictionary keys and values will be permanently cached in memory the first time they are seen, instead of being fetched from the file when needed. Yes, this means objects will grow in memory, potentially very large. Only use this if it's an acceptable pay-off for much faster access. πŸ”₯
⚠️ This is not yet implemented for Arrays.

Returns the "root node" of the database. If that root node is a number, some piece of text, True, False, or Null, then it's decoded and the value returned. Otherwise you get an object (possibly a tied object) representing an Array or a Dictionary. 🎁


πŸ‘‘ OBJECTS πŸ‘‘

If you asked for normal objects instead of tied objects, then these are sub-classes of either Data::CompactReadonly::Array or Data::CompactReadonly::Dictionary. Both implement the following three methods:

πŸ’œ id

Returns a unique id for this object within the database. Note that circular data structures are supported, and looking at the id is the only way to detect them. πŸ‘€

Not accessible when using tied objects.

πŸ’œ count

Returns the number of elements in the structure.

πŸ’œ indices

Returns a list of all the available indices in the structure.

πŸ’œ element

Takes a single argument, which must match one of the values that would be returned by indices, and returns the associated data.

If the data is a number, Null, or text, the value will be returned directly. If the data is in turn another array or dictionary, an object will be returned.

πŸ’œ exists

Takes a single argument and tells you whether an index exists for it. It will still die if you ask it for something stupid, such as a floating point array index or a Null dictionary entry. Be normal about it. 😭


🚫 UNSUPPORTED PERL TYPES 🚫

  • Globs
  • Regexes
  • References (except to Arrays and Dictionaries)

Booleans are only supported on perl version 5.35.7 or later. On earlier perls, a Boolean in the database will be decoded as a true or false value, but its type will be numeric or string. And older perls will never write a True or False node to the database β€” they'll always write numbers or strings with true/false values, which other implementations will decode as numbers or strings. πŸ’€


πŸ› BUGS/FEEDBACK πŸ›

Please report bugs at <https://github.com/DrHyde/perl-modules-Data-CompactReadonly/issues> β€” including a test case if possible, bestie, it helps so much. 🫢


πŸ‘€ SEE ALSO πŸ‘€

DBM::Deep β€” if you need updateable databases.


🧚 SOURCE CODE REPOSITORY 🧚

<git://github.com/DrHyde/perl-modules-Data-CompactReadonly.git>


Copyright 2023 David Cantrell <david@cantrell.org.uk> πŸ’–

This software is free-as-in-speech software, and may be used, distributed, and modified under the terms of either the GNU General Public Licence version 2 or the Artistic Licence. It's up to you which one you use. The full text of the licences can be found in the files GPL2.txt and ARTISTIC.txt, respectively.


🌈 CONSPIRACY 🌈

This module is also free-as-in-mason software. 🧱✨


okay that's the whole page bestie, go forth and read your databases πŸ’… β€” this was perl v5.36.0, dated 2023-10-30, and honestly it ate. πŸŽ€πŸ¦‹πŸ‘‘

πŸ“¦ where this page comes from