{"id":26336276,"url":"https://github.com/point-platform/cassette","last_synced_at":"2026-02-25T07:33:32.432Z","repository":{"id":29550421,"uuid":"33089565","full_name":"point-platform/cassette","owner":"point-platform","description":"A simple content-addressable storage system for .NET 4.5 and .NET Core","archived":false,"fork":false,"pushed_at":"2024-02-01T05:27:19.000Z","size":278,"stargazers_count":56,"open_issues_count":0,"forks_count":8,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-12-07T20:08:59.605Z","etag":null,"topics":["associative-storage","c-sharp","cas","content-addressable-storage","dotnet","dotnet-core"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/point-platform.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-29T21:12:11.000Z","updated_at":"2024-08-08T07:39:51.000Z","dependencies_parsed_at":"2024-06-21T16:53:07.026Z","dependency_job_id":null,"html_url":"https://github.com/point-platform/cassette","commit_stats":null,"previous_names":["drewnoakes/cassette"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/point-platform%2Fcassette","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/point-platform%2Fcassette/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/point-platform%2Fcassette/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/point-platform%2Fcassette/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/point-platform","download_url":"https://codeload.github.com/point-platform/cassette/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243809859,"owners_count":20351407,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["associative-storage","c-sharp","cas","content-addressable-storage","dotnet","dotnet-core"],"created_at":"2025-03-16T01:17:21.681Z","updated_at":"2026-02-25T07:33:27.401Z","avatar_url":"https://github.com/point-platform.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Cassette](Cassette.png)\n\n[![Cassette NuGet version](https://img.shields.io/nuget/v/DrewNoakes.Cassette.svg)](https://www.nuget.org/packages/DrewNoakes.Cassette/)\n\nCassette is a simple and efficient content-addressable storage system for .NET 4.5 and .NET Core (`netstandard1.3`).\n\n```csharp\n// Create a store, backed by the specified file system location\nvar cassette = new ContentAddressableStore(@\"c:\\cassette-data\\\");\n\n// Store some content, obtaining its hash (content address)\nHash hash = await cassette.WriteAsync(writeStream);\n\n// Later, use the hash to look up the content\nStream stream;\nif (cassette.TryRead(hash, out stream, ReadOptions.Asynchronous | ReadOptions.SequentialScan))\n{\n    using (stream)\n    {\n        // Read the stored content via the returned read-only stream\n        var buffer = new byte[4096];\n        var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);\n        // ...\n    }\n}\n```\n\nContent-addressable storage (CAS) is a fast and efficient mechanism for storing and retrieving fixed data on disk.\n\nInformation is uniquely and unambiguously identified by the SHA-1 hash of its contents.\n\nA significant advantage of CAS is its efficient use of storage media for data backups where a majority of files are identical, and so separate storage would be redundant.\n\nFor more information, read [Wikipedia's CAS article](http://en.wikipedia.org/wiki/Content-addressable_storage).\n\n---\n\n## Types\n\n* `IContentAddressableStore` exposes functionality of a cassette store\n* `ContentAddressableStore` is the concrete implementation\n* `Hash` holds the identity of a piece of content\n\n## Writing content\n\n```csharp\nusing (var stream = File.OpenRead(@\"c:\\content.jpg\"))\n{\n    var hash = await store.WriteAsync(dataStream);\n    Console.WriteLine(\"Stored with address: {0}\", hash);\n}\n```\n\nThe hash is computed efficiently by observing content during buffered writes to a temporary location of the file system. Then:\n\n* if the content does not already exist, the file is atomically moved into location and marked as read-only\n* if the content does already exist, the temporary file is deleted and the store is unchanged\n\nThis means writes are idempotent. Repeated writes of the same content do not increase the size of the store on disk, though do incur disk IO.\n\n## Reading\n\nRead operations atomically test for the availability of a file and open it for reading if present.\n\nMultiple clients may read a file concurrently. Content may not be deleted via `IContentAddressableStore.Delete` while a read stream is open.\n\nWhen `TryRead` returns `true`, client code must dispose the returned `Stream`.\n\n```csharp\nHash hash = ...;\nStream stream;\nif (store.TryRead(hash, out stream))\n{\n    using (stream)\n    {\n        // Use the stream\n    }\n}\n```\n\nThe performance of reads can be improved by specifying `ReadOptions` as described in sections [async IO](#Asynchronous-IO) and [access patterns and performance](#Access-patterns-and-performance).\n\n* `None` indicates no special options. This is the default.\n* `SequentialScan` indicates data will be read sequentially.\n* `RandomAccess` indicates seek operations will be performed during reading.\n* `Asynchronous` indicates `Stream.ReadAsync` will be used.\n\n```csharp\nstore.TryRead(hash, out stream, ReadOptions.SequentialScan | ReadOptions.ReadAsync)\n```\n\n## Asynchronous IO\n\nWrite operations happen using asynchronous IO and return awaitable tasks to prevent blocking calling code. Use of a `CancellationToken` allows immediate cancellation of the write operation.\n\nRead operations occur using `Stream`s which support asynchronous IO themselves via `ReadAsync` and `CopyToAsync`. When using these asynchronous methods you can improve performance by passing `ReadOptions.Asynchronous` to `IContentAddressableStore.TryRead`.\n\nOn Windows these asynchronous operations use IO Completion ports ([MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx), [Dr. Dobbs](http://www.drdobbs.com/cpp/multithreaded-asynchronous-io-io-comple/201202921)). Other platforms may have implementations using suitable alternatives.\n\n## Access patterns and performance\n\nWhen writing content to disk, cassette notifies the file system that data will be written sequentially and that no random-access seeking will occur. This allows the caching system to prepare pages of data efficiently which can significantly reduce latency.\n\nWhen reading content from disk, users can get the same caching benefits by specifying `ReadOptions.SequentialScan` or `ReadOptions.RandomAccess`. This is optional however.\n\n## Deleting content\n\nContent may be deleted from the store by calling `Delete` with the relevant hash.\n\n## Measuring content length\n\nIf the length of stored content is to be retrieved, it is most efficient to use `TryGetContentLength` which provides the length in bytes.\n\n## Enumerating content\n\nThe complete set of hashes is returned via `GetHashes`. This method computes the enumerable lazily by walking the file system so is thread-safe with respect to reads and writes. However it cannot be relied upon to behave deterministically if enumerating while content is being written or deleted. Whether new or deleted content is included in an enumeration whose processing spans the write/delete may or may not contain the affected content.\n\n## Encoding\n\nCassette supports optionally storing content using an encoding. The primary use case for this is to store pre-compressed data whereby the cost of compressing content is taken upfront once at write time, rather than for each read.\n\nContent may be stored in multiple encodings. For example, an HTTP server may support both no encoding, or GZIP content/transfer encoding. Such an HTTP handler could request either encoding from the store depending upon request headers.\n\n```csharp\n// Instantiate a content encoding\nvar gzipEncoding = new GZipContentEncoding();\n\n// Write some (unencoded) content to the store, and request an encoded copy be stored\nvar hash = await store.WriteAsync(stream, encodings: new[] { gzipEncoding });\n\n// Read the encoded content out directly\nStream gzipStream;\nif (store.TryRead(hash, out gzipStream, encodingName: gzipEncoding.Name))\n{\n    using (gzipStream)\n    {\n        // Use the GZipped data directly\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoint-platform%2Fcassette","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoint-platform%2Fcassette","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoint-platform%2Fcassette/lists"}