From 3899ed4b80e0b698c4676923d2f8572bc83bf2ad Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 30 Oct 2025 05:14:05 +0100 Subject: [PATCH 1/3] Initial commit with task details for issue #508 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: undefined --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..9f796c980 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: undefined +Your prepared branch: issue-508-5a1549d7 +Your prepared working directory: /tmp/gh-issue-solver-1761797619230 + +Proceed. \ No newline at end of file From 217e0a90287fce659519b37ec3fc520e409efa46 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 30 Oct 2025 05:25:31 +0100 Subject: [PATCH 2/3] Add LinksExternalReferenceValidator to allow treating links references as binary data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements issue #508 by creating a new validator that accepts both internal and external references: - Internal references (existing links) are validated for existence - External references (binary data via Hybrid type) are allowed without validation Changes: 1. Added EnsureReferenceExistsOrExternal extension methods to ILinksExtensions.cs - Validates internal references must exist in store - Allows external references (binary data) without existence checking 2. Created LinksExternalReferenceValidator decorator - Similar structure to LinksInnerReferenceExistenceValidator - Uses new EnsureReferenceExistsOrExternal validation methods - Enables doublets store to be used as file system/binary data storage 3. Added comprehensive test suite (7 tests, all passing) - Tests internal reference validation - Tests external reference support - Tests mixed internal/external references - Tests Each, Update, and Delete operations - Demonstrates real-world binary data storage scenario This enables variable-length reference sequences and allows the doublets store to function as a file system, archive, or file storage database as described in issue #508. Fixes #508 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../LinksExternalReferenceValidatorTests.cs | 262 ++++++++++++++++++ .../LinksExternalReferenceValidator.cs | 105 +++++++ .../ILinksExtensions.cs | 69 +++++ 3 files changed, 436 insertions(+) create mode 100644 csharp/Platform.Data.Doublets.Tests/LinksExternalReferenceValidatorTests.cs create mode 100644 csharp/Platform.Data.Doublets/Decorators/LinksExternalReferenceValidator.cs diff --git a/csharp/Platform.Data.Doublets.Tests/LinksExternalReferenceValidatorTests.cs b/csharp/Platform.Data.Doublets.Tests/LinksExternalReferenceValidatorTests.cs new file mode 100644 index 000000000..f6b38138d --- /dev/null +++ b/csharp/Platform.Data.Doublets.Tests/LinksExternalReferenceValidatorTests.cs @@ -0,0 +1,262 @@ +using System; +using System.Numerics; +using Platform.Data.Doublets.Decorators; +using Platform.Data.Doublets.Memory; +using Platform.Data.Doublets.Memory.United.Generic; +using Platform.Data.Exceptions; +using Platform.Memory; +using Xunit; + +namespace Platform.Data.Doublets.Tests +{ + public static class LinksExternalReferenceValidatorTests + { + [Fact] + public static void InternalReferenceValidationTest() + { + // This test verifies that internal references must exist in the store + var memory = new HeapResizableDirectMemory(); + var links = new UnitedMemoryLinks(memory); + var validator = new LinksExternalReferenceValidator(links); + + // Create a valid link + var link1 = validator.Create(); + var link2 = validator.Create(); + + // Update with valid internal references should succeed + validator.Update(link1, link1, link2); + + // Try to update with non-existent internal reference should throw + var nonExistentInternalRef = (uint)999; + Assert.True(links.Constants.IsInternalReference(nonExistentInternalRef)); + Assert.False(links.Exists(nonExistentInternalRef)); + + Assert.Throws>(() => + { + validator.Update(link1, nonExistentInternalRef, link2); + }); + + memory.Dispose(); + } + + [Fact] + public static void ExternalReferenceAllowedTest() + { + // This test verifies that external references (binary data) are allowed without existence validation + var memory = new HeapResizableDirectMemory(); + + // Configure with explicit internal and external ranges + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + var validator = new LinksExternalReferenceValidator(links); + + // Create a valid link + var link1 = validator.Create(); + + // Use external reference (binary data) - should NOT throw even if it doesn't exist + // External references represent raw binary data, not links in the store + // Use values within internal range but marked as external via Hybrid (like LinksConstantsTests) + // Use a large value that's unlikely to exist as a link ID + var externalRef = new Hybrid(long.MaxValue - 1000, isExternal: true); + + Assert.True(links.Constants.IsExternalReference(externalRef)); + // Note: We don't assert !Exists() because the value might coincidentally exist + // The key test is that Update succeeds without throwing for external references + + // This should succeed because external references are allowed + var updatedLink = validator.Update(link1, externalRef, externalRef); + + Assert.True(updatedLink > default(ulong)); + + memory.Dispose(); + } + + [Fact] + public static void MixedInternalAndExternalReferencesTest() + { + // This test verifies mixed usage of internal and external references + var memory = new HeapResizableDirectMemory(); + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + var validator = new LinksExternalReferenceValidator(links); + + // Create valid internal links + var internalLink1 = validator.Create(); + var internalLink2 = validator.Create(); + + // Create external references (binary data) + // Use values within internal range but marked as external via Hybrid + var externalRef1 = new Hybrid(100, isExternal: true); + var externalRef2 = new Hybrid(200, isExternal: true); + + // Verify classifications + Assert.True(links.Constants.IsInternalReference(internalLink1)); + Assert.True(links.Constants.IsExternalReference(externalRef1)); + + // Update with internal → external should succeed + var link3 = validator.Update(internalLink1, internalLink2, externalRef1); + Assert.True(link3 > default(ulong)); + + // Update with external → external should succeed + var link4 = validator.Create(); + var link5 = validator.Update(link4, externalRef1, externalRef2); + Assert.True(link5 > default(ulong)); + + // Update with internal (non-existent) should still throw + var nonExistentInternal = (ulong)999; + Assert.True(links.Constants.IsInternalReference(nonExistentInternal)); + + Assert.Throws>(() => + { + validator.Update(internalLink1, nonExistentInternal, externalRef1); + }); + + memory.Dispose(); + } + + [Fact] + public static void EachOperationWithExternalReferencesTest() + { + // This test verifies the Each operation with external references + var memory = new HeapResizableDirectMemory(); + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + var validator = new LinksExternalReferenceValidator(links); + + // Create links with external references + var externalRef = new Hybrid(500, isExternal: true); + var internalLink = validator.Create(); + + var linkWithExternal = validator.Update(internalLink, externalRef, externalRef); + + // Query using Each with external reference in restriction should work + int count = 0; + validator.Each(new[] { linkWithExternal }, link => + { + count++; + return links.Constants.Continue; + }); + + Assert.Equal(1, count); + + memory.Dispose(); + } + + [Fact] + public static void DeleteOperationValidationTest() + { + // This test verifies delete operation still validates link existence + var memory = new HeapResizableDirectMemory(); + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + var validator = new LinksExternalReferenceValidator(links); + + // Create a link + var link1 = validator.Create(); + + // Delete existing link should succeed + validator.Delete(link1); + + // Try to delete non-existent link should throw + Assert.Throws>(() => + { + validator.Delete(link1); + }); + + memory.Dispose(); + } + + [Fact] + public static void ComparisonWithInnerReferenceValidatorTest() + { + // This test demonstrates the difference between LinksInnerReferenceExistenceValidator + // and LinksExternalReferenceValidator + var memory = new HeapResizableDirectMemory(); + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + + var innerValidator = new LinksInnerReferenceExistenceValidator(links); + var externalValidator = new LinksExternalReferenceValidator(links); + + var internalLink = links.Create(); + var externalRef = new Hybrid(999, isExternal: true); + + // Inner validator only accepts internal references + // External reference should be treated as valid by EnsureInnerReferenceExists + // because it checks IsInternalReference first + var link2 = innerValidator.Update(internalLink, externalRef, externalRef); + Assert.True(link2 > default(ulong)); + + // External validator also accepts external references explicitly + var link3 = links.Create(); + var link4 = externalValidator.Update(link3, externalRef, externalRef); + Assert.True(link4 > default(ulong)); + + memory.Dispose(); + } + + [Fact] + public static void BinaryDataStorageScenarioTest() + { + // This test demonstrates the real-world use case from issue #508: + // Using the doublets store as a file system or binary data storage + var memory = new HeapResizableDirectMemory(); + var constants = new LinksConstants( + (1, long.MaxValue), + (long.MaxValue + 1UL, ulong.MaxValue) + ); + var links = new UnitedMemoryLinks(memory, UnitedMemoryLinks.DefaultLinksSizeStep, constants, IndexTreeType.Default); + var validator = new LinksExternalReferenceValidator(links); + + // Scenario: Store file-like data where references can be binary data + // Create a "file type" marker link + var fileTypeMarker = validator.Create(); + + // Create a "file" link where source is the type and target is binary metadata + var binaryMetadata = new Hybrid(0xDEADBEEF, isExternal: true); + Assert.True(links.Constants.IsExternalReference(binaryMetadata)); + + var fileLink = validator.Create(); + var updatedFileLink = validator.Update(fileLink, fileTypeMarker, binaryMetadata); + + Assert.True(updatedFileLink > default(ulong)); + + // Create another link with binary data for file content pointer + var contentPointer = new Hybrid(0x12345678, isExternal: true); + var contentLink = validator.Create(); + var updatedContentLink = validator.Update(contentLink, updatedFileLink, contentPointer); + + Assert.True(updatedContentLink > default(ulong)); + + // Verify we can query these links + int filesFound = 0; + validator.Each(link => + { + if (links.GetSource(link) == fileTypeMarker || links.GetSource(link) == updatedFileLink) + { + filesFound++; + } + return links.Constants.Continue; + }); + + Assert.True(filesFound >= 2); + + memory.Dispose(); + } + } +} diff --git a/csharp/Platform.Data.Doublets/Decorators/LinksExternalReferenceValidator.cs b/csharp/Platform.Data.Doublets/Decorators/LinksExternalReferenceValidator.cs new file mode 100644 index 000000000..cd1964a2d --- /dev/null +++ b/csharp/Platform.Data.Doublets/Decorators/LinksExternalReferenceValidator.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using Platform.Delegates; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +namespace Platform.Data.Doublets.Decorators; + +/// +/// +/// Represents the links external reference validator. +/// A layer that checks each link to exist or to be external (hybrid link's raw number). +/// This validator allows references to be treated as binary data when they are external references. +/// +/// +/// +/// +public class LinksExternalReferenceValidator : LinksDecoratorBase where TLinkAddress : IUnsignedNumber +{ + /// + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// A links. + /// + /// + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + public LinksExternalReferenceValidator(ILinks links) : base(links: links) { } + + /// + /// + /// Eaches the handler. + /// + /// + /// + /// + /// The handler. + /// + /// + /// + /// The restriction. + /// + /// + /// + /// The link + /// + /// + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + public override TLinkAddress Each(IList? restriction, ReadHandler? handler) + { + var links = _links; + links.EnsureReferenceExistsOrExternal(restriction: restriction, argumentName: nameof(restriction)); + return links.Each(restriction: restriction, handler: handler); + } + + /// + /// + /// Updates the restriction. + /// + /// + /// + /// + /// The restriction. + /// + /// + /// + /// The substitution. + /// + /// + /// + /// The link + /// + /// + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + public override TLinkAddress Update(IList? restriction, IList? substitution, WriteHandler? handler) + { + var links = _links; + links.EnsureReferenceExistsOrExternal(restriction: restriction, argumentName: nameof(restriction)); + links.EnsureReferenceExistsOrExternal(restriction: substitution, argumentName: nameof(substitution)); + return links.Update(restriction: restriction, substitution: substitution, handler: handler); + } + + /// + /// + /// Deletes the restriction. + /// + /// + /// + /// + /// The restriction. + /// + /// + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + public override TLinkAddress Delete(IList? restriction, WriteHandler? handler) + { + var links = _links; + var link = links.GetIndex(link: restriction); + links.EnsureLinkExists(link: link, argumentName: nameof(link)); + return links.Delete(restriction: restriction, handler: handler); + } +} diff --git a/csharp/Platform.Data.Doublets/ILinksExtensions.cs b/csharp/Platform.Data.Doublets/ILinksExtensions.cs index a94c8f579..fe13d085a 100644 --- a/csharp/Platform.Data.Doublets/ILinksExtensions.cs +++ b/csharp/Platform.Data.Doublets/ILinksExtensions.cs @@ -601,6 +601,75 @@ public static void EnsureInnerReferenceExists(this ILinks + /// + /// Ensures the reference exists or is external using the specified links. + /// Validates that internal references exist in the store, while allowing external references (binary data) without validation. + /// + /// + /// + /// + /// The link. + /// + /// + /// + /// The links. + /// + /// + /// + /// The reference. + /// + /// + /// + /// The argument name. + /// + /// + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EnsureReferenceExistsOrExternal(this ILinks links, TLinkAddress reference, string argumentName) where TLinkAddress : IUnsignedNumber + { + if (links.Constants.IsInternalReference(reference) && !links.Exists(reference)) + { + throw new ArgumentLinkDoesNotExistsException(reference, argumentName); + } + // External references are allowed without existence validation (they represent binary data) + } + + /// + /// + /// Ensures the references exist or are external using the specified links. + /// Validates that internal references exist in the store, while allowing external references (binary data) without validation. + /// + /// + /// + /// + /// The link. + /// + /// + /// + /// The links. + /// + /// + /// + /// The restriction. + /// + /// + /// + /// The argument name. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EnsureReferenceExistsOrExternal(this ILinks links, IList? restriction, string argumentName) where TLinkAddress : IUnsignedNumber + { + for (int i = 0; i < restriction.Count; i++) + { + links.EnsureReferenceExistsOrExternal(restriction[i], argumentName); + } + } + /// /// /// Ensures the link is any or exists using the specified links. From a525b516e6ae5114e9d176a37bb4ae76a636dbfc Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 30 Oct 2025 05:27:25 +0100 Subject: [PATCH 3/3] Revert "Initial commit with task details for issue #508" This reverts commit 3899ed4b80e0b698c4676923d2f8572bc83bf2ad. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 9f796c980..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: undefined -Your prepared branch: issue-508-5a1549d7 -Your prepared working directory: /tmp/gh-issue-solver-1761797619230 - -Proceed. \ No newline at end of file