Getting started
This page is a cross-library tour. It installs the Bodu packages, then for each library gives a one-minute orientation, a minimal working sample, and pointers to that library's introduction and getting-started guide. The sections follow the suite's seven topics - each topic heading links to its topic overview page.
If you only need a single library, jump straight to its section below - each one ends with links to its dedicated Introduction, Getting started, and Guides pages.
Prerequisites
- The .NET 8 SDK - every runtime package in the solution targets
net8.0. (The one exception is the build-timeBodu.Globalization.Calendar.BuildMSBuild task package, which targetsnetstandard2.0so it loads inside any MSBuild host.)
dotnet --version
Install
Each package is versioned and released independently; install only the ones you need. Bodu.Core is the common foundation and is pulled in automatically. A few packages layer on other Bodu packages - Bodu.Financial on Bodu.Numerics, Bodu.Formats.Excel.Binary on Bodu.IO.Compound and Bodu.IO.Biff, Bodu.IO.Pst on Bodu.Collections, every serializer on Bodu.Text.Serialization - and NuGet resolves those transitively too; the package matrix lists each package's dependencies.
# Core Foundations
dotnet add package Bodu.Core
dotnet add package Bodu.Collections
dotnet add package Bodu.Collections.Concurrent
# Hashing & Cryptography
dotnet add package Bodu.IO.Hashing
dotnet add package Bodu.Security.Cryptography
# Globalization & Calendars
dotnet add package Bodu.Globalization.Calendar
dotnet add package Bodu.Globalization.Recurrence
# Optional region-specific calendar data packs:
dotnet add package Bodu.Globalization.Calendar.Americas
dotnet add package Bodu.Globalization.Calendar.Europe
dotnet add package Bodu.Globalization.Calendar.AsiaPacific
dotnet add package Bodu.Globalization.Calendar.Africa
dotnet add package Bodu.Globalization.Calendar.MiddleEast
# Text & Serialization
dotnet add package Bodu.Text.Encoding
dotnet add package Bodu.Text.Filtering
dotnet add package Bodu.Text.Formats
dotnet add package Bodu.Text.Toml
dotnet add package Bodu.Text.Bencode
dotnet add package Bodu.Text.Yaml
# Configuration
dotnet add package Bodu.Text.Configuration
dotnet add package Bodu.Extensions.Configuration.Text
# Numerics & Financial
dotnet add package Bodu.Numerics
dotnet add package Bodu.Financial
# Optional companions:
dotnet add package Bodu.Financial.DependencyInjection
# Binary Formats & I/O
dotnet add package Bodu.IO.Compound
dotnet add package Bodu.IO.Biff
dotnet add package Bodu.Formats.Excel.Binary
dotnet add package Bodu.IO.Pst
dotnet add package Bodu.Formats.Outlook.Msg
dotnet add package Bodu.Formats.Outlook.Pst
Core Foundations
The foundation of the suite - see the Core Foundations overview for how every other topic builds on it.
Bodu.Core
Bodu.Core is the foundation package - the WeekPattern value type, pooled buffers, async coordination primitives, railway outcomes (Option<T> / Result<T>), and date / numeric / span extensions sitting on a centralized ThrowHelper. It is the one package every other Bodu library depends on.
using Bodu;
WeekPattern weekdays = WeekPattern.Parse("_MTWTF_");
WeekPattern weekend = WeekPattern.Parse("S_____S");
WeekPattern allDays = weekdays | weekend;
bool monday = weekdays.Contains(DayOfWeek.Monday); // true
WeekPattern is an immutable 7-bit bitmask value type for sets of days of the week - compose with the bitwise operators, parse from compact text, and enumerate the selected days in order.
→ Introduction · Getting started · Guides
Bodu.Collections
Bodu.Collections is the specialized collection catalogue (it depends on Bodu.Core; the namespaces are unchanged) - fixed-capacity rings, policy-driven caches with TTL expiry, navigable and range-keyed lookups, interval trees, graphs, tries, and probabilistic sketches.
using Bodu.Collections.Generic;
var buffer = new CircularBuffer<int>(capacity: 4, allowOverwrite: true);
buffer.Enqueue(1);
buffer.Enqueue(2);
buffer.Enqueue(3);
buffer.Enqueue(4);
buffer.Enqueue(5); // 1 is evicted; buffer holds [2, 3, 4, 5]
int oldest = buffer.Dequeue(); // 2
CircularBuffer<T> is a fixed-capacity FIFO collection. With allowOverwrite: true it silently drops the oldest item when full; with allowOverwrite: false it throws instead.
→ Introduction · Getting started · Guides
Bodu.Collections.Concurrent
Bodu.Collections.Concurrent ships the thread-safe members of the catalogue (it depends on Bodu.Collections) - the lock-free ConcurrentCircularBuffer<T> and the lock-free split-ordered ConcurrentHashSet<T>, both with snapshot enumeration that never throws on concurrent modification, and the lock-striped ConcurrentEvictingDictionary<TKey,TValue> bounded cache with all six eviction policies, optional TTL, and single-flight GetOrAdd.
using Bodu.Collections.Generic.Concurrent;
var seen = new ConcurrentHashSet<string>(StringComparer.OrdinalIgnoreCase);
Parallel.ForEach(events, e =>
{
if (seen.Add(e.CorrelationId)) // true only for the first arrival
ProcessFirstOccurrence(e);
});
Contains is lock-free - readers never block writers - and disjoint writers proceed in parallel across independently locked bucket regions.
→ Introduction · Getting started · Guides
Bodu.Text
The Bodu.Text namespace - shipped in the Bodu.Core package - adds the ergonomic, allocation-aware surface the BCL leaves out on top of System.Text.Encoding - byte-order-mark detection, span- and UTF-8-friendly transcoding, preamble handling, and validation. (For binary-to-text codecs such as Base64, reach for Bodu.Text.Encoding below.)
using Bodu.Text;
byte[] bytes = File.ReadAllBytes("data.txt");
System.Text.Encoding encoding =
EncodingDetection.TryDetectByPreamble(bytes, out System.Text.Encoding? detected)
? detected
: System.Text.Encoding.UTF8;
string text = encoding.GetStringSkippingPreamble(bytes);
// Going the other way: encode without an intermediate byte[] when the destination is a span.
Span<byte> buffer = stackalloc byte[text.GetUtf8ByteCount()];
int written = text.EncodeUtf8To(buffer);
byte[] withBom = text.ToBytesWithPreamble(System.Text.Encoding.UTF8);
EncodingDetection.TryDetectByPreamble is non-allocating and recognises the five canonical Unicode BOMs; GetStringSkippingPreamble decodes the payload while dropping any leading preamble. Going the other way, StringEncodingExtensions sizes and writes UTF-8 (or any encoding) straight into a span or IBufferWriter<byte> - GetUtf8ByteCount / EncodeUtf8To above, plus TryEncodeUtf8To, WriteUtf8To, the pooled GetUtf8BytesPooled, and their Encoding-parameterised twins - and ToBytesWithPreamble emits the BOM the detector will recognise. The EncodingExtensions surface adds the chunked and Try* overloads for the hot paths.
→ Introduction · API reference
Hashing & Cryptography
One question splits the two packages - is there an adversary? - see the Hashing & Cryptography overview for the decision rule.
Bodu.IO.Hashing
Bodu.IO.Hashing covers non-cryptographic hashing - fingerprints for hash-table keys, checksums for error detection, and check digits for human-typed identifiers. Every type shares the BCL Append / GetCurrentHash / Reset lifecycle, and nothing here is safe against an adversary who can choose the input.
using System.Text;
using Bodu.IO.Hashing;
using Bodu.IO.Hashing.Checksums;
byte[] data = Encoding.UTF8.GetBytes("the quick brown fox");
using var crc = new Crc(CrcStandard.CRC32_ISOHDLC);
crc.Append(data);
string hex = Convert.ToHexString(crc.GetCurrentHash());
Swap CRC32_ISOHDLC for any of the 113 entries in the CRC catalogue, or pick a fingerprint (Fnv1a64, CityHash64, MurmurHash3_128) for hash-table use.
→ Introduction · Getting started · Guides
Bodu.Security.Cryptography
Bodu.Security.Cryptography provides cryptographic primitives with a formal adversary model - block ciphers, AEAD modes, keyed hashes, and cryptographic digests - all on the standard SymmetricAlgorithm / HashAlgorithm contracts, so they drop into any code that already speaks .NET cryptography.
using System.Security.Cryptography;
using System.Text;
using Bodu.Security.Cryptography;
byte[] data = Encoding.UTF8.GetBytes("the quick brown fox");
byte[] key = RandomNumberGenerator.GetBytes(16);
using var sip = new SipHash64 { Key = key };
ulong digest = BitConverter.ToUInt64(sip.ComputeHash(data));
SipHash64 derives from System.Security.Cryptography.HashAlgorithm, so it drops into any API that expects a standard .NET hash. For block ciphers, AEAD modes, the ASCON family, and the RFC 6962 MerkleTree with its inclusion and consistency proofs, see the per-library pages below.
→ Introduction · Getting started · Guides
Globalization & Calendars
The notable-date runtime plus its companions and regional data packs - see the Globalization & Calendars overview for how the package family composes.
Bodu.Globalization.Calendar
Bodu.Globalization.Calendar resolves notable dates - public holidays, observances, religious festivals - for a date, range, or year and territory, from rule documents loaded into an immutable resource and queried through NotableDateService.
using Bodu.Globalization.Calendar;
NotableDateService service = AsiaPacificCalendarData.CreateService("AU");
foreach (NotableDate d in service.Resolve(2026, "AU-NSW"))
Console.WriteLine($"{d.Date:yyyy-MM-dd} {d.DisplayName}");
For authoring rule documents, territory filtering, the observance-adjustment pipeline, working-day arithmetic, and the regional data packs, see the getting-started page below.
→ Introduction · Getting started · Guides
Bodu.Globalization.Recurrence
Bodu.Globalization.Recurrence answers when does this repeat? in four shapes - RFC 5545 RRULE, an RDATE / EXDATE composed RecurrenceSet, Vixie-style CronExpression, and the instant-anchored AnchoredInterval. It depends only on Bodu.Core, so it is a sibling of the calendar engine rather than a dependant, and it is pure in its arguments - no wall clock, no machine time zone.
using Bodu.Globalization.Recurrence;
RecurrenceRule rule = RecurrenceRule.Parse("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR");
var start = new DateTime(2026, 1, 5, 9, 0, 0); // Monday 5 Jan 2026, 09:00
// Every form answers both directions, with inclusive flags.
DateTime? next = rule.GetNextOccurrence(start, after: new DateTime(2026, 1, 20));
DateTime? previous = rule.GetPreviousOccurrence(start, before: new DateTime(2026, 1, 20));
// next = 2026-01-21 09:00 (Wednesday of the second fortnight)
// previous = 2026-01-19 09:00 (Monday)
foreach (DateTime occurrence in rule.GetOccurrences(start, new DateTime(2026, 2, 1), new DateTime(2026, 2, 28)))
Console.WriteLine(occurrence); // 2, 4, 6, 16, 18, 20 Feb - all 09:00
Build rules fluently with RecurrenceRuleBuilder, compose exceptions into a RecurrenceSet, or swap the grammar for cron - every form carries a defect-naming TryParse(s, out result, out failureMessage) overload.
→ Introduction · Getting started · Guides
Text & Serialization
Three different jobs that all sound like "text" - see the Text & Serialization overview for the codec / document-format / serializer disambiguation.
Bodu.Text.Encoding
Bodu.Text.Encoding is a library of binary-to-text encoders - Base16, Base32, Base64, Base58, and Base85 - each with span- and UTF-8-friendly overloads, OperationStatus streaming, and a unified IBinaryEncoding interface for runtime-pluggable selection.
using Bodu.Text.Encoding;
byte[] digest = SHA256.HashData("hello"u8.ToArray());
string hex = Base16.Encode(digest); // canonical lower case
string hexUpper = Base16.Encode(digest, BaseFormattingOptions.UpperCase); // RFC 4648 §8 canonical case
string base32 = Base32.Encode(digest); // RFC 4648 §6 (padded)
string base64 = Base64.Encode(digest); // RFC 4648 §4 (padded)
string urlSafe = Base64.Encode(digest, Base64Variant.UrlSafe); // RFC 4648 §5 (no padding)
Pick the encoding from configuration at runtime via the unified interface:
IBinaryEncoding encoding = BinaryEncodings.Get("base64-urlsafe");
string token = encoding.Encode(rawBytes);
byte[] back = encoding.Decode(token);
For lenient parsing (whitespace, 0x prefix, missing padding), OperationStatus streaming, and per-variant options like MIME line wrapping or Crockford alias decoding, see the getting-started page below.
→ Introduction · Getting started · Guides
Bodu.Text.Filtering
Bodu.Text.Filtering filters lists of text values through include/exclude pattern sets - glob and regex patterns compiled once into a cost-tiered TextFilter, with Ant / MSBuild set semantics or gitignore-style ordered rules, and built-in telemetry reporting what matched and why.
using Bodu.Text.Filtering;
var filter = TextFilter.Parse(
[
"error*", // include everything starting with "error"
"{warn,fatal}*",
"!*debug*", // '!' flips the pattern to an exclude
]);
var kept = filter.FilterToList(lines); // bulk filtering, source order preserved
var why = filter.Evaluate("error-debug"); // → Excluded, decided by "!*debug*"
→ Introduction · Getting started · Guides
Bodu.Text.Formats
Bodu.Text.Formats parses and emits self-framing serialization formats - Delimited (CSV / TSV), DotEnv, and Ini - each through a strongly-typed value model and a span- and stream-friendly codec with Try* overloads.
using Bodu.Text.Ini;
IniObject config = IniNode.Parse("""
; connection settings
[database]
host = localhost
port = 5432
"""u8);
config["database"].AsObject()["port"].AsValue().Value = "5433";
string text = config.ToString(); // comments and ordering preserved
The parsed model retains comments, ordering, and whitespace, so a Parse → mutate → Format cycle round-trips the source. The Delimited and DotEnv namespaces follow the same Parse / Format shape.
→ Introduction · Getting started · Guides
Bodu.Text.Bencode, Bodu.Text.Toml, and Bodu.Text.Yaml (serializers)
Bodu.Text.Bencode, Bodu.Text.Toml, and Bodu.Text.Yaml are three libraries that map your own types to and from a format. They share the Bodu.Text.Serialization engine and a System.Text.Json-aligned shape, and each ships a serializer, two document object models, and a low-level Utf8…Reader / Utf8…Writer pair.
using Bodu.Text.Toml;
public sealed class ServerConfig
{
public string Host { get; set; } = "";
public int Port { get; set; }
}
string toml = TomlSerializer.Serialize(new ServerConfig { Host = "localhost", Port = 8080 });
ServerConfig config = TomlSerializer.Deserialize<ServerConfig>(toml);
Swap Toml for Bencode (and string for byte[]) for the Bencode equivalent, or for Yaml for a YAML document. Bencode (BEP 3) object mapping lives here, not in Bodu.Text.Formats.
→ Introduction · Bencode · TOML · YAML · Guides
Configuration
Layered, EditorConfig-style configuration and its Microsoft.Extensions.Configuration bridge - see the Configuration overview for the full pipeline.
Bodu.Text.Configuration
Bodu.Text.Configuration layers EditorConfig-style configuration. It parses INI / EditorConfig text, resolves a preamble plus glob-anchored sections in source order for a target file path, and projects the result into a flat, colon-delimited ConfigurationView with typed accessors.
using Bodu.Text.Configuration;
const string source = """
root = true
[*.cs]
format.indent.size = 4
[src/**/*.cs]
format.indent.size = 2
""";
ConfigurationView view = ConfigurationDocument
.Parse(source)
.Resolve("src/App/Program.cs");
int indentSize = view.GetInt32("format:indent:size"); // 2 - the last matching section wins
Resolve walks the document once in source order, layering the preamble and every glob-matching section; the flat ConfigurationView then exposes typed accessors - GetInt32, GetBoolean, GetEnum<T>, GetValue<T> - plus optional diagnostic collection and byte-faithful round-trip save.
→ Introduction · Getting started · Guides
Bodu.Extensions.Configuration.Text
Bodu.Extensions.Configuration.Text bridges Bodu.Text.Configuration into Microsoft.Extensions.Configuration. An AddTextConfiguration* builder call layers a Bodu configuration file alongside JSON, INI, XML, and environment-variable sources, with IOptions<T> binding and reload-on-change support.
using Microsoft.Extensions.Configuration;
using Bodu.Extensions.Configuration.Text;
IConfiguration config = new ConfigurationBuilder()
.AddTextConfigurationFile("app.editorconfig", optional: true, reloadOnChange: true)
.Build();
string? indentSize = config["format:indent:size"];
AddTextConfigurationFile mirrors AddJsonFile - the Bodu source layers into the standard provider stack and participates in IOptions<T> binding, so existing Microsoft.Extensions.Configuration code adopts it with no learning curve.
→ Introduction · Getting started · Guides
Numerics & Financial
Exact arithmetic and the monetary primitives built on it - see the Numerics & Financial overview for how Money rides on Fraction<BigInteger>.
Bodu.Numerics
Bodu.Numerics ships two generic-math value types - Fraction<T> for exact rational arithmetic and Interval<T> for bounded intervals - both built on INumber<T> so they compose with any generic-math algorithm.
using Bodu.Numerics;
Fraction<int> total = Fraction<int>.Create(1, 3) + Fraction<int>.Create(2, 3); // 1/1
Interval<int> range = Interval<int>.Closed(1, 10);
bool containsFive = range.Contains(5); // true
Every Fraction<T> is GCD-reduced on construction and promotes to BigInteger for exact intermediates; Interval<T> expresses closed / open / half-open bounds with intersection, union, and adjacency operations.
→ Introduction · Getting started · Guides
Bodu.Financial
Bodu.Financial provides type-safe monetary primitives: Money for runtime-tagged amounts, and Money<TCurrency> where the currency is encoded as the type parameter so cross-currency arithmetic fails the build.
using Bodu.Financial;
using Bodu.Financial.Currencies;
// Runtime-tagged money - currency carried as a CurrencyCode.
Money price = new Money(125.50m, CurrencyCode.AUD);
Money gst = price * 0.10m;
Money total = price + gst; // 138.05 AUD
// Compile-time currency safety - the currency is the type parameter.
Money<AUD> typed = new Money<AUD>(125.50m);
Mixing currencies on the typed form is a compile error; on the runtime form it throws. For service registration, the companion Bodu.Financial.DependencyInjection package wires currency lookup and monetary services into an IServiceCollection:
using Bodu.Financial;
services.AddFinancialService();
→ Introduction · Getting started · Guides
Binary Formats & I/O
Legacy binary container and document formats - a read/edit/author compound-file container with narrower read-only format readers on top; see the Binary Formats & I/O overview for the layered container-vs-format split.
Bodu.IO.Compound
Bodu.IO.Compound reads, edits, and authors the OLE2 / Compound File Binary (CFB) container behind legacy Office documents, exposing the embedded named streams with no application-format knowledge.
using Bodu.IO.Compound;
using CompoundFile file = CompoundFile.Open(File.OpenRead("book.xls"));
// Walk the storage hierarchy.
foreach (CompoundEntryInfo info in file.RootStorage.EnumerateEntries())
Console.WriteLine($"{info.EntryType}: {info.Name} ({info.Length} bytes)");
// Read a named stream's bytes.
CompoundStream workbook = file.RootStorage.OpenStream("Workbook");
byte[] bytes = workbook.ReadAllBytes();
Open with buffered: false to read sectors on demand for large files; OpenStream(name) returns a seekable CompoundStream cursor. The BIFF5 and BIFF8 .xls reader Bodu.Formats.Excel.Binary is built on top of this container reader, decoding the Workbook stream's records through Bodu.IO.Biff.
→ Introduction · Getting started · Guides
Bodu.IO.Biff
Bodu.IO.Biff is the record-stream codec beneath the .xls reader - a forward-only, allocation-free ref struct over a span. It frames every physical record, establishes the version from BOF and the code page from CODEPAGE, and decodes the structural and cell records through typed accessors. Unknown records are never an error.
using Bodu.IO.Biff;
using Bodu.IO.Compound;
using CompoundFile container = CompoundFile.OpenRead("report.xls");
using CompoundStream workbook = container.RootStorage.OpenStream("Workbook");
byte[] bytes = new byte[workbook.Length];
workbook.ReadExactly(bytes);
var reader = new BiffReader(bytes);
while (reader.Read())
Console.WriteLine($"{reader.RecordType,-14} id=0x{reader.RecordId:X4} length={reader.RecordLength}");
BiffReaderState resumes framing across buffers, BiffSstReader walks the shared string table over its CONTINUE records, and BiffWriter emits BIFF5 or BIFF8 records. There is no container dependency and no workbook model - that is the next package up.
→ Introduction · Getting started
Bodu.Formats.Excel.Binary
Bodu.Formats.Excel.Binary is the read-only workbook reader over those two layers: raw worksheet cell values - strings, numbers, booleans, errors, and a formula cell's cached result - with each sheet's declared used range. No formula evaluation, no styling.
using Bodu.Formats.Excel;
using ExcelBinaryWorkbook workbook = ExcelBinaryWorkbook.OpenRead("rates.xls");
foreach (ExcelWorksheetInfo sheet in workbook.Worksheets)
Console.WriteLine($"{sheet.Index}: {sheet.Name} - {sheet.Dimensions.RowCount} × {sheet.Dimensions.ColumnCount}");
OpenWorksheet(name) returns a forward-only ExcelWorksheetReader for streaming, and ReadWorksheet(name) materializes an addressable ExcelWorksheet when random access is easier.
→ Introduction · Getting started · Guides
Bodu.IO.Pst
Bodu.IO.Pst is the container layer for Outlook personal-folders files (Unicode and ANSI) - the node database and the LTP layer over it, with the block encodings decoded and the checksums verified. It exposes wire-typed values, not MAPI semantics, and never writes.
using Bodu.IO.Pst;
using PstFile file = PstFile.OpenRead("archive.pst");
PstNode store = file.GetNode(PstNodeId.MessageStore);
foreach (PstPropertyValue value in store.ReadPropertyContext())
Console.WriteLine($"0x{value.PropertyId:X4} (wire 0x{value.WireType:X4}): {value.RawData.Length} bytes");
EnumerateNodes() walks the node B-tree, and each PstNode offers its raw bytes, its subnodes, and the two LTP views - ReadPropertyContext() and ReadTableContext().
→ Introduction · Getting started · Guides
Bodu.Formats.Outlook
Bodu.Formats.Outlook is the shared MAPI value model; Bodu.Formats.Outlook.Msg reads a .msg message over Bodu.IO.Compound, and Bodu.Formats.Outlook.Pst reads a .pst mail store over Bodu.IO.Pst. Both are read-only and both surface the same recipient, attachment, and body types.
using Bodu.Formats.Outlook;
using var message = OutlookMessage.OpenRead("invoice.msg");
Console.WriteLine(message.Subject);
Console.WriteLine($"{message.SenderName} <{message.SenderEmailAddress}>");
foreach (OutlookRecipient recipient in message.Recipients)
Console.WriteLine($"{recipient.RecipientType}: {recipient.DisplayName} <{recipient.EmailAddress}>");
foreach (OutlookAttachment attachment in message.Attachments)
Console.WriteLine($"{attachment.Method}: {attachment.FileName} ({attachment.Size} bytes)");
OutlookMailStore.OpenRead(path) opens a store instead, giving RootFolder and streaming folder and message enumeration; IsMsgFile / IsPstFile sniff a stream when the format is not known up front.
→ Introduction · Getting started · Guides
Where to go next
- Introduction - what each library is for and how they fit together.
- Topic overviews: Core Foundations · Hashing & Cryptography · Globalization & Calendars · Text & Serialization · Configuration · Numerics & Financial · Binary Formats & I/O.
- Library introductions: Bodu.Core · Bodu.Collections · Bodu.Collections.Concurrent · Bodu.IO.Hashing · Bodu.Security.Cryptography · Bodu.Globalization.Calendar · Bodu.Globalization.Recurrence · Bodu.Text.Encoding · Bodu.Text.Filtering · Bodu.Text.Formats · Bodu.Text.Bencode · Bodu.Text.Toml · Bodu.Text.Yaml · Bodu.Text.Configuration · Bodu.Extensions.Configuration.Text · Bodu.Text · Bodu.Numerics · Bodu.Financial · Bodu.IO.Compound · Bodu.IO.Biff · Bodu.Formats.Excel.Binary · Bodu.IO.Pst · Bodu.Formats.Outlook.
- API references: Bodu.Collections.Generic · Bodu.IO.Hashing · Bodu.Security.Cryptography · Bodu.Globalization.Calendar · Bodu.Text · Bodu.Numerics · Bodu.Financial · Bodu.IO.Compound · Bodu.IO.Biff · Bodu.Formats.Excel · Bodu.IO.Pst · Bodu.Formats.Outlook · Bodu.Globalization.Recurrence.