WpfHexEditorControl

Wpf Hexeditor is a powerful and fully customisable user control for editing file or stream as hexadecimal, decimal and binary. Can be used in Wpf or WinForm application

View project on GitHub

WPF HexEditor - Architecture Documentation

This document provides visual diagrams and detailed architecture documentation for the WPF HexEditor Control project.

πŸ“‹ Table of Contents

  1. Solution Structure
  2. Service Layer Architecture
  3. Core Components Architecture
  4. Data Flow
  5. Class Relationships
  6. Component Dependencies
  7. Performance Considerations

πŸ—οΈ Solution Structure

graph TB
    subgraph "Solution: WpfHexEditorControl"
        subgraph "Main Library"
            WPFHexaEditor[WPFHexaEditor.dll<br/>WpfHexEditorCore.csproj]

            subgraph "Services Layer (10 Services)"
                ClipboardSvc[ClipboardService]
                FindReplaceSvc[FindReplaceService]
                UndoRedoSvc[UndoRedoService]
                SelectionSvc[SelectionService]
                HighlightSvc[HighlightService]
                ByteModSvc[ByteModificationService]
                BookmarkSvc[BookmarkService]
                TblSvc[TblService]
                PositionSvc[PositionService]
                CustomBgSvc[CustomBackgroundService]
            end

            subgraph "Core Layer"
                ByteProvider[ByteProvider]
                ByteModified[ByteModified]
                TBL[Character Tables]
                Interfaces[Interfaces]
                Converters[Converters]
            end

            subgraph "UI Layer"
                HexEditor[HexEditor Control]
                HexByte[HexByte Control]
                StringByte[StringByte Control]
                Dialogs[Dialogs]
            end
        end

        subgraph "Sample Applications"
            SampleCS[WPFHexEditor.Sample.CSharp]
            SampleVB[WpfHexEditor.Sample.VB]
            SampleBarChart[WpfHexEditor.Sample.BarChart]
            SampleWinform[WpfHexEditor.Sample.Winform]
            SampleAvalonDock[WpfHexEditor.Sample.AvalonDock]
            SampleInsert[WpfHexEditor.Sample.InsertByteAnywhere]
            SampleDiff[WpfHexEditor.Sample.BinaryFilesDifference]
            SampleServiceUsage[WpfHexEditor.Sample.ServiceUsage<br/>Console Demo]
        end

        subgraph "Testing"
            UnitTests[WPFHexaEditor.Tests<br/>xUnit Test Project<br/>80+ Tests]
        end

        subgraph "Tools"
            ByteProviderBench[ByteProviderBench<br/>Performance Testing]
        end
    end

    HexEditor --> ClipboardSvc
    HexEditor --> FindReplaceSvc
    HexEditor --> UndoRedoSvc
    HexEditor --> SelectionSvc
    HexEditor --> HighlightSvc
    HexEditor --> ByteModSvc
    HexEditor --> BookmarkSvc
    HexEditor --> TblSvc
    HexEditor --> PositionSvc
    HexEditor --> CustomBgSvc

    ClipboardSvc --> ByteProvider
    FindReplaceSvc --> ByteProvider
    UndoRedoSvc --> ByteProvider
    SelectionSvc --> ByteProvider
    ByteModSvc --> ByteProvider
    PositionSvc --> ByteProvider

    HexEditor --> ByteProvider
    HexEditor --> TBL
    HexEditor --> Dialogs

    SampleCS --> WPFHexaEditor
    SampleVB --> WPFHexaEditor
    SampleBarChart --> WPFHexaEditor
    SampleWinform --> WPFHexaEditor
    SampleAvalonDock --> WPFHexaEditor
    SampleInsert --> WPFHexaEditor
    SampleDiff --> WPFHexaEditor

    ByteProviderBench --> ByteProvider

    style WPFHexaEditor fill:#e1f5ff
    style HexEditor fill:#fff9c4
    style ClipboardSvc fill:#c8e6c9
    style FindReplaceSvc fill:#c8e6c9
    style UndoRedoSvc fill:#c8e6c9
    style SelectionSvc fill:#c8e6c9
    style HighlightSvc fill:#b3e5fc
    style ByteModSvc fill:#b3e5fc
    style BookmarkSvc fill:#f8bbd0
    style TblSvc fill:#f8bbd0
    style PositionSvc fill:#f8bbd0
    style CustomBgSvc fill:#f8bbd0
    style ByteProvider fill:#ffccbc

🎯 Service Layer Architecture (10 Services)

graph LR
    subgraph "HexEditor.xaml.cs (Main WPF Controller)"
        HE[HexEditor Control<br/>UI Coordination & Events]
    end

    subgraph "Services Layer (Business Logic) - 10 Services"
        subgraph "Core Services (6)"
            CS[πŸ“‹ ClipboardService<br/>Copy/Paste/Cut]
            FRS[πŸ” FindReplaceService<br/>Search & Replace<br/>+ Cache 5sec]
            URS[↩️ UndoRedoService<br/>History Management]
            SS[🎯 SelectionService<br/>Validation]
            HS[✨ HighlightService<br/>Search Highlights]
            BMS[πŸ”§ ByteModificationService<br/>Insert/Delete/Modify]
        end

        subgraph "Additional Services (4)"
            BKS[πŸ”– BookmarkService<br/>Bookmark Management]
            TBS[πŸ“š TblService<br/>Character Tables]
            PS[πŸ“ PositionService<br/>Position Calc]
            CBS[🎨 CustomBackgroundService<br/>Background Blocks]
        end
    end

    subgraph "Data Layer"
        BP[ByteProvider<br/>File/Stream Access]
        FS[File System]
        MEM[Memory Stream]
    end

    HE -->|Uses| CS
    HE -->|Uses| FRS
    HE -->|Uses| URS
    HE -->|Uses| SS
    HE -->|Uses| HS
    HE -->|Uses| BMS
    HE -->|Uses| BKS
    HE -->|Uses| TBS
    HE -->|Uses| PS
    HE -->|Uses| CBS

    CS -->|Delegates to| BP
    FRS -->|Delegates to| BP
    URS -->|Delegates to| BP
    SS -->|Delegates to| BP
    BMS -->|Delegates to| BP

    BP -->|Reads/Writes| FS
    BP -->|Reads/Writes| MEM

    style HE fill:#fff9c4
    style CS fill:#c8e6c9
    style FRS fill:#c8e6c9
    style URS fill:#c8e6c9
    style SS fill:#c8e6c9
    style HS fill:#b3e5fc
    style BMS fill:#b3e5fc
    style BKS fill:#f8bbd0
    style TBS fill:#f8bbd0
    style PS fill:#f8bbd0
    style CBS fill:#f8bbd0
    style BP fill:#ffccbc

Service Responsibilities

Service Type Responsibility Key Operations
ClipboardService Stateless Clipboard operations Copy, Paste, FillWithByte, CanCopy, CanDelete
FindReplaceService Stateless Search & Replace + Cache FindFirst, FindNext, FindAll, ReplaceAll, ClearCache
UndoRedoService Stateless History management Undo, Redo, CanUndo, CanRedo, GetUndoCount
SelectionService Stateless Selection validation ValidateSelection, GetSelectionLength, GetSelectionBytes
HighlightService Stateful Search highlighting (HashSet, batching, 10-100x faster) AddHighLight, AddHighLightRanges, BeginBatch/EndBatch, IsHighlighted
ByteModificationService Stateless Byte operations ModifyByte, InsertByte, InsertBytes, DeleteBytes, DeleteRange
BookmarkService Stateful Bookmark management AddBookmark, GetNextBookmark, GetPreviousBookmark, HasBookmarkAt
TblService Stateful Character table management LoadFromFile, LoadDefault, BytesToString, FindMatch
PositionService Stateless Position calculations GetLineNumber, GetColumnNumber, HexLiteralToLong, LongToHex
CustomBackgroundService Stateful Background color blocks AddBlock, GetBlockAt, GetBlocksInRange, RemoveBlocksAt

πŸ”§ Core Components Architecture

graph TB
    subgraph "Core Components"
        subgraph "Bytes Package"
            BP[ByteProvider<br/>Main Data Access]
            BM[ByteModified<br/>Undo/Redo Entry]
            B8[Byte_8bit]
            B16[Byte_16bit]
            B32[Byte_32bit]
            BC[ByteConverters<br/>Hex/Dec/Bin]
            BD[ByteDifference<br/>File Comparison]
        end

        subgraph "Character Table"
            TBL[TblStream<br/>Custom Character Maps]
            TBLF[.tbl File Format]
        end

        subgraph "Interfaces"
            IByte[IByte]
            IByteControl[IByteControl]
            IByteModified[IByteModified]
        end

        subgraph "UI Components"
            BK[BookMark<br/>User Bookmarks]
            CBB[CustomBackgroundBlock<br/>Color Regions]
            Caret[Caret<br/>Text Cursor]
            RB[RandomBrushes<br/>Color Generator]
        end

        subgraph "Support"
            Enum[Enumerations<br/>ByteAction, CopyPasteMode, etc.]
            Const[Constants<br/>Default Values]
            KV[KeyValidator<br/>Input Validation]
            Conv[WPF Converters<br/>Data Binding]
        end
    end

    BP --> BM
    BP --> IByte
    BM --> IByteModified

    B8 --> IByte
    B16 --> IByte
    B32 --> IByte

    TBL --> TBLF

    style BP fill:#ffccbc
    style BM fill:#ffccbc
    style TBL fill:#e1bee7
    style BK fill:#c5cae9
    style CBB fill:#c5cae9

πŸ”„ Data Flow

Read Operation Flow

sequenceDiagram
    participant User
    participant HexEditor
    participant SelectionService
    participant ByteProvider
    participant FileStream

    User->>HexEditor: Click on byte
    HexEditor->>SelectionService: GetSelectionByte(position)
    SelectionService->>ByteProvider: GetByte(position)
    ByteProvider->>FileStream: ReadByte()
    FileStream-->>ByteProvider: byte value
    ByteProvider-->>SelectionService: (byte, success)
    SelectionService-->>HexEditor: byte?
    HexEditor-->>User: Display byte

Write Operation Flow

sequenceDiagram
    participant User
    participant HexEditor
    participant UndoRedoService
    participant ByteProvider
    participant FileStream

    User->>HexEditor: Modify byte (type "FF")
    HexEditor->>ByteProvider: AddByteModified(old, new, pos)
    ByteProvider->>ByteProvider: Push to UndoStack
    ByteProvider->>FileStream: WriteByte(position, new)
    FileStream-->>ByteProvider: Success
    ByteProvider-->>HexEditor: Modified
    HexEditor->>HexEditor: RefreshView()
    HexEditor-->>User: Show updated byte

    Note over User,FileStream: Undo Operation
    User->>HexEditor: Press Ctrl+Z
    HexEditor->>UndoRedoService: Undo(_provider)
    UndoRedoService->>ByteProvider: Undo()
    ByteProvider->>ByteProvider: Pop from UndoStack
    ByteProvider->>ByteProvider: Push to RedoStack
    ByteProvider->>FileStream: WriteByte(position, old)
    FileStream-->>ByteProvider: Success
    ByteProvider-->>UndoRedoService: position
    UndoRedoService-->>HexEditor: position
    HexEditor->>HexEditor: SetFocusAt(position)
    HexEditor-->>User: Byte restored

Find Operation Flow (with Cache)

sequenceDiagram
    participant User
    participant HexEditor
    participant FindReplaceService
    participant Cache
    participant ByteProvider
    participant FileStream

    User->>HexEditor: Find "Hello"
    HexEditor->>FindReplaceService: FindFirst(data)
    FindReplaceService->>Cache: Check cache

    alt Cache Hit
        Cache-->>FindReplaceService: Cached results
        FindReplaceService-->>HexEditor: position
    else Cache Miss
        FindReplaceService->>ByteProvider: FindIndexOf(data)
        ByteProvider->>FileStream: Search in stream
        FileStream-->>ByteProvider: positions[]
        ByteProvider-->>FindReplaceService: positions[]
        FindReplaceService->>Cache: Store results (5sec TTL)
        FindReplaceService-->>HexEditor: position
    end

    HexEditor->>HexEditor: SetPosition(position)
    HexEditor->>HexEditor: AddHighLight(position)
    HexEditor-->>User: Highlight found bytes

    Note over User,FileStream: User modifies data
    User->>HexEditor: Modify any byte
    HexEditor->>FindReplaceService: ClearCache()
    FindReplaceService->>Cache: Clear all cached results
    Note over Cache: Cache invalidated!

πŸ”— Class Relationships

classDiagram
    class HexEditor {
        -ByteProvider _provider
        -ClipboardService _clipboardService
        -FindReplaceService _findReplaceService
        -UndoRedoService _undoRedoService
        -SelectionService _selectionService
        +long SelectionStart
        +long SelectionStop
        +string FileName
        +CopyToClipboard()
        +FindFirst()
        +Undo()
        +Redo()
    }

    class ByteProvider {
        -Stream _stream
        -Stack~ByteModified~ _undoStack
        -Stack~ByteModified~ _redoStack
        +long Length
        +bool IsOpen
        +GetByte(position)
        +AddByteModified()
        +Undo()
        +Redo()
        +FindIndexOf()
    }

    class ClipboardService {
        +CopyPasteMode DefaultCopyMode
        +CopyToClipboard()
        +CanCopy()
        +FillWithByte()
    }

    class FindReplaceService {
        -byte[] _lastSearchData
        -IEnumerable~long~ _lastSearchResults
        -long _lastSearchTimestamp
        +FindFirst()
        +FindAll()
        +ReplaceAll()
        +ClearCache()
    }

    class UndoRedoService {
        +Undo(provider, repeat)
        +Redo(provider, repeat)
        +CanUndo()
        +CanRedo()
        +GetUndoCount()
    }

    class SelectionService {
        +IsValidSelection()
        +GetSelectionLength()
        +ValidateSelection()
        +GetSelectionBytes()
    }

    class ByteModified {
        +byte Byte
        +long BytePositionInStream
        +ByteAction Action
    }

    HexEditor --> ByteProvider
    HexEditor --> ClipboardService
    HexEditor --> FindReplaceService
    HexEditor --> UndoRedoService
    HexEditor --> SelectionService
    HexEditor --> HighlightService
    HexEditor --> ByteModificationService
    HexEditor --> BookmarkService
    HexEditor --> TblService
    HexEditor --> PositionService
    HexEditor --> CustomBackgroundService

    ClipboardService ..> ByteProvider : uses
    FindReplaceService ..> ByteProvider : uses
    UndoRedoService ..> ByteProvider : uses
    SelectionService ..> ByteProvider : uses
    ByteModificationService ..> ByteProvider : uses
    PositionService ..> ByteProvider : uses

    ByteProvider --> ByteModified : creates

πŸ“¦ Component Dependencies

graph TD
    subgraph "Dependency Layers"
        subgraph "Layer 1: UI"
            HE[HexEditor]
            Dialogs[Dialogs]
            Controls[HexByte, StringByte]
        end

        subgraph "Layer 2: Services (10 Total)"
            subgraph "Core Services"
                SVC1[ClipboardService]
                SVC2[FindReplaceService]
                SVC3[UndoRedoService]
                SVC4[SelectionService]
                SVC5[HighlightService]
                SVC6[ByteModificationService]
            end
            subgraph "Additional Services"
                SVC7[BookmarkService]
                SVC8[TblService]
                SVC9[PositionService]
                SVC10[CustomBackgroundService]
            end
        end

        subgraph "Layer 3: Core"
            BP[ByteProvider]
            TBL[TblStream]
            Enums[Enumerations]
            Interfaces[Interfaces]
            BM[BookMark]
            CBB[CustomBackgroundBlock]
        end

        subgraph "Layer 4: Infrastructure"
            IO[System.IO]
            WPF[WPF Framework]
            NET[.NET Framework/Core]
        end
    end

    HE --> SVC1
    HE --> SVC2
    HE --> SVC3
    HE --> SVC4
    HE --> SVC5
    HE --> SVC6
    HE --> SVC7
    HE --> SVC8
    HE --> SVC9
    HE --> SVC10
    HE --> BP
    HE --> TBL
    HE --> WPF

    Dialogs --> HE
    Controls --> BP

    SVC1 --> BP
    SVC2 --> BP
    SVC3 --> BP
    SVC4 --> BP

    BP --> IO
    BP --> Interfaces
    BP --> Enums

    TBL --> IO

    IO --> NET
    WPF --> NET

    style HE fill:#fff9c4
    style SVC1 fill:#c8e6c9
    style SVC2 fill:#c8e6c9
    style SVC3 fill:#c8e6c9
    style SVC4 fill:#c8e6c9
    style BP fill:#ffccbc
    style NET fill:#e0e0e0

Dependency Rules

  1. UI Layer can depend on Services, Core, and Infrastructure
  2. Services Layer can only depend on Core and Infrastructure
  3. Core Layer can only depend on Infrastructure
  4. Infrastructure Layer has no internal dependencies

Benefits:

  • Clear separation of concerns
  • Testable components (services don’t depend on UI)
  • Maintainable codebase
  • Easy to add new features

🎨 Copy/Paste Mode Support

graph LR
    subgraph "CopyPasteMode Enum"
        HexStr[HexaString<br/>FF 00 AB]
        AsciiStr[ASCIIString<br/>Hello]
        TblStr[TBLString<br/>Custom chars]
        CSharp["CSharpCode<br/>new byte[...]"]
        VBNet["VBNetCode<br/>Dim bytes As Byte"]
        C["CCode<br/>unsigned char data"]
        Java["JavaCode<br/>byte data"]
        FSharp["FSharpCode<br/>let bytes = array"]
    end

    User[User Copies] --> ClipboardService
    ClipboardService --> HexStr
    ClipboardService --> AsciiStr
    ClipboardService --> TblStr
    ClipboardService --> CSharp
    ClipboardService --> VBNet
    ClipboardService --> C
    ClipboardService --> Java
    ClipboardService --> FSharp

    style ClipboardService fill:#c8e6c9

πŸ” Search Cache Strategy

stateDiagram-v2
    [*] --> Idle

    Idle --> Searching: FindFirst/FindAll called
    Searching --> CacheCheck: Check cache validity

    CacheCheck --> CacheHit: Data matches & TTL valid
    CacheCheck --> CacheMiss: Data differs or expired

    CacheHit --> ReturnCached: Return cached results
    CacheMiss --> PerformSearch: Search in ByteProvider

    PerformSearch --> StoreCache: Store results (5sec TTL)
    StoreCache --> ReturnResults: Return fresh results

    ReturnCached --> Idle
    ReturnResults --> Idle

    Idle --> CacheInvalidation: Data modified
    CacheInvalidation --> CacheCleared: ClearCache() called
    CacheCleared --> Idle

    note right of CacheCheck
        Cache valid if:
        - Same search data
        - TTL < 5 seconds
    end note

    note right of CacheInvalidation
        Triggered by:
        - ModifyByte
        - Paste
        - Insert
        - Delete
        - Replace
        - Undo
    end note

πŸ“Š Performance Optimization Points

graph TB
    subgraph "Performance Critical Paths"
        A[User Scrolls View]
        B[ByteProvider.GetByte<br/>⚑ Optimized with cache]
        C[UI Virtualization<br/>⚑ Only render visible bytes]

        D[User Searches Large File]
        E[FindReplaceService.FindAll<br/>⚑ Cached results 5sec]
        F[ByteProvider.FindIndexOf<br/>⚑ Boyer-Moore algorithm]

        G[User Modifies Bytes]
        H["UndoRedoService<br/>⚑ Stack operations O(1)"]
        I[ByteProvider.AddByteModified<br/>⚑ Minimal overhead]
    end

    A --> B
    B --> C

    D --> E
    E --> F

    G --> H
    H --> I

    style B fill:#c8e6c9
    style C fill:#c8e6c9
    style E fill:#c8e6c9
    style F fill:#ffccbc
    style H fill:#c8e6c9
    style I fill:#ffccbc

Performance Targets

Operation Target Achieved
GetByte() < 1 ΞΌs βœ… ~0.5 ΞΌs
FindFirst (1MB) < 50 ms βœ… ~30 ms
Undo/Redo < 100 ΞΌs βœ… ~50 ΞΌs
Paste 1KB < 10 ms βœ… ~5 ms
UI Render (1000 bytes) < 16 ms (60fps) βœ… ~10 ms

πŸ§ͺ Testing Architecture

graph TB
    subgraph "Testing Strategy"
        subgraph "Unit Tests (10 Services + Core + Performance)"
            UT1[ClipboardService Tests]
            UT2[FindReplaceService Tests]
            UT3[UndoRedoService Tests]
            UT4[SelectionService Tests]
            UT5[HighlightService Tests]
            UT6[ByteModificationService Tests]
            UT7[BookmarkService Tests]
            UT8[TblService Tests]
            UT9[PositionService Tests]
            UT10[CustomBackgroundService Tests]
            UT11[ByteProvider Tests]
            UT12[SpanSearchExtensions Tests<br/>18 tests βœ…]
            UT13[SpanSearchSIMD Tests<br/>18 tests βœ…]
            UT14[ByteProviderOptimized Tests<br/>17 tests βœ…]
        end

        subgraph "Integration Tests"
            IT1[HexEditor + Services Tests]
            IT2[File I/O Tests]
            IT3[TBL Loading Tests]
        end

        subgraph "Performance Benchmarks"
            BM1[SpanBenchmarks<br/>Traditional vs Span vs Pool]
            BM2[AsyncBenchmarks<br/>Sync vs Async operations]
            BM3[SIMDBenchmarks<br/>Scalar vs SSE2 vs AVX2]
            BM4[ByteProviderBench<br/>Legacy tool]
        end

        subgraph "Sample Applications"
            Samples[8 Sample Apps<br/>Manual Testing]
            PerfSample[WpfHexEditor.Sample.Performance<br/>Interactive Demos]
        end
    end

    UT1 -.-> IT1
    UT2 -.-> IT1
    UT3 -.-> IT1
    UT4 -.-> IT1
    UT5 -.-> IT1
    UT6 -.-> IT1
    UT7 -.-> IT1
    UT8 -.-> IT1
    UT9 -.-> IT1
    UT10 -.-> IT1
    UT11 -.-> IT1

    UT12 -.-> BM1
    UT13 -.-> BM3
    UT14 -.-> BM1

    IT1 -.-> Samples
    IT2 -.-> Samples
    IT3 -.-> Samples

    BM1 -.-> PerfSample
    BM2 -.-> PerfSample
    BM3 -.-> PerfSample

    style UT1 fill:#c8e6c9
    style UT2 fill:#c8e6c9
    style UT3 fill:#c8e6c9
    style UT4 fill:#c8e6c9
    style UT5 fill:#b3e5fc
    style UT6 fill:#b3e5fc
    style UT7 fill:#f8bbd0
    style UT8 fill:#f8bbd0
    style UT9 fill:#f8bbd0
    style UT10 fill:#f8bbd0
    style UT11 fill:#ffccbc
    style UT12 fill:#c8e6c9
    style UT13 fill:#c8e6c9
    style UT14 fill:#c8e6c9
    style BM1 fill:#fff9c4
    style BM2 fill:#fff9c4
    style BM3 fill:#fff9c4
    style PerfSample fill:#ffe0b2

Test Coverage (v2.2+)

Unit Tests: 53 tests (all passing)

  • SpanSearchExtensionsTests: 18 tests
    • FindIndexOf (multi-byte patterns)
    • FindFirstIndexOf (early termination)
    • CountOccurrences (zero-allocation counting)
    • Edge cases (empty data, overlapping matches, etc.)
  • SpanSearchSIMDTests: 18 tests
    • FindFirstSIMD (single-byte, AVX2/SSE2)
    • FindAllSIMD (vectorized search)
    • CountOccurrencesSIMD (SIMD counting)
    • FindAll2BytePatternSIMD (hybrid approach)
    • Hardware detection and fallback
    • Consistency with standard methods
  • ByteProviderOptimizedSearchTests: 17 tests
    • FindIndexOfOptimized (chunked search with ArrayPool)
    • FindFirstOptimized (early-exit search)
    • CountOccurrencesOptimized (optimized counting)
    • Chunk boundary handling
    • Start position support

Performance Benchmarks:

  • SpanBenchmarks - Traditional vs Span with ArrayPool
  • AsyncBenchmarks - Sync vs async operations
  • SIMDBenchmarks - Scalar vs SSE2 vs AVX2 comparison

Test Frameworks:

  • xUnit 2.6.6
  • BenchmarkDotNet 0.13.x
  • Microsoft.NET.Test.Sdk 17.8.0

πŸ“ Summary

Key Architectural Decisions

  1. Service-Based Architecture (2026 Refactoring)
    • Extracted business logic from HexEditor class
    • Created 10 specialized services (6 stateless, 4 stateful)
    • ~2500+ lines of business logic extracted
    • Improved testability, maintainability, and reusability
    • Zero breaking changes to public API
  2. Provider Pattern
    • ByteProvider abstracts file/stream access
    • Supports different backends (file, memory, network)
    • Centralized data access point
  3. Command Pattern for Undo/Redo
    • ByteModified objects represent commands
    • Stack-based history management
    • Memory-efficient
  4. Caching Strategy
    • 5-second TTL for search results
    • Automatic invalidation on data changes
    • Balances performance and correctness
  5. UI Virtualization
    • Only render visible bytes
    • Supports files > 1GB
    • Maintains 60fps scrolling

Migration Path

Completed:

  • βœ… Service-based architecture fully implemented
  • βœ… 10 services created and integrated (6 stateless, 4 stateful)
  • βœ… Critical bug fix (search cache invalidation)
  • βœ… All core services: ClipboardService, FindReplaceService, UndoRedoService, SelectionService
  • βœ… All specialized services: HighlightService, ByteModificationService
  • βœ… All additional services: BookmarkService, TblService, PositionService, CustomBackgroundService
  • βœ… ~2500+ lines of business logic extracted
  • βœ… API preserved with no breaking changes (zero breaking changes)
  • βœ… 0 compilation errors, 0 warnings

Next Steps:

  • πŸ“‹ Add comprehensive unit tests for all 10 services
  • βœ… Performance profiling and optimization (Completed 2026)
  • βœ… Add async variants for file I/O heavy operations (Completed 2026)
  • βœ… Add Span zero-allocation extensions (Completed 2026)
  • βœ… Add SIMD vectorization (AVX2/SSE2) (Completed 2026)
  • βœ… Add BenchmarkDotNet performance suite (Completed 2026)
  • βœ… Add 53 unit tests for performance optimizations (Completed 2026)
  • βœ… Add comprehensive performance guide (Completed 2026)
  • βœ… Add UI virtualization service (Completed 2026)
  • πŸ“‹ Consider event system for service state changes

⚑ Performance Optimization Architecture (v2.2+)

πŸ“– Complete Guide: See PERFORMANCE_GUIDE.md for comprehensive documentation with examples, benchmarks, and migration guides.

Overview

WPF HexEditor v2.2+ includes three tiers of performance optimizations that deliver 10-40x faster operations with 95% less memory allocation:

Tier Technology Speed Gain Memory Savings Availability
Tier 1 Span<byte> + ArrayPool 2-5x 90% net48, net8.0+
Tier 2 Async/Await ∞ (UI responsive) Minimal net48, net8.0+
Tier 3 SIMD (AVX2/SSE2) 4-8x N/A net5.0+ only

Combined Performance Results

When all three tiers are applied:

  • 10-40x faster than traditional implementations
  • 95% less memory allocation
  • 100% UI responsiveness during long operations
  • Scalable to GB-sized files

Real-World Example:

Operation: Count occurrences of byte 0x00 in 5MB file
- Traditional: 65ms, 50MB allocated, UI frozen
- Optimized: 3.2ms, 1MB allocated, UI responsive
- Result: 20.3x faster, 98% less memory

Three-Tier Optimization System

Tier 1: Span<byte> + ArrayPool (net48, net8.0+)

  • Zero-allocation memory operations
  • Buffer pooling with ArrayPool
  • 2-5x faster execution
  • 90% less memory allocation
  • 80% fewer GC collections

Tier 2: Async/Await (net48, net8.0+)

  • Non-blocking I/O operations
  • UI stays responsive during long operations
  • Progress reporting with IProgress<int>
  • Cancellation support with CancellationToken
  • Infinite responsiveness improvement

Tier 3: SIMD Vectorization (net5.0+ only)

  • AVX2 (256-bit) - processes 32 bytes at once
  • SSE2 (128-bit) - processes 16 bytes at once
  • 4-8x faster single-byte searches
  • Automatic fallback to scalar on older CPUs
  • Hardware intrinsics via System.Runtime.Intrinsics

Performance Architecture Layers

  1. Span<byte> Extensions - Zero-allocation memory operations
  2. Async/Await Extensions - Non-blocking I/O operations
  3. SIMD Extensions - Hardware-accelerated vectorized search
  4. UI Virtualization Service - Memory-efficient rendering
graph TB
    subgraph "Performance Optimization Layers"
        subgraph "UI Layer (WPF Controls)"
            HexEditor[HexEditor Control<br/>Main UI]
            HexByte[HexByte Controls]
            StringByte[StringByte Controls]
        end

        subgraph "Performance Services"
            VirtualizationSvc[VirtualizationService<br/>Memory-Efficient Rendering]

            subgraph "ByteProvider Extensions"
                SpanExt[ByteProviderSpanExtensions<br/>Zero-Allocation Operations]
                AsyncExt[ByteProviderAsyncExtensions<br/>Non-Blocking I/O]
            end
        end

        subgraph "Core Data Access"
            BP[ByteProvider<br/>File/Stream Access]
        end

        subgraph "System Resources"
            ArrayPoolSys[ArrayPool&lt;byte&gt;<br/>Buffer Reuse]
            TaskSys[Task Scheduler<br/>Thread Pool]
            FileSystem[File System<br/>I/O]
        end
    end

    HexEditor -->|Uses for viewport calc| VirtualizationSvc
    HexEditor -->|Renders only visible| HexByte
    HexEditor -->|Renders only visible| StringByte

    VirtualizationSvc -->|Calculates visible lines| BP

    SpanExt -->|Zero-copy reads| BP
    SpanExt -->|Rents buffers from| ArrayPoolSys

    AsyncExt -->|Non-blocking reads| BP
    AsyncExt -->|Schedules on| TaskSys

    BP -->|Reads/Writes| FileSystem

    style VirtualizationSvc fill:#b3e5fc
    style SpanExt fill:#c8e6c9
    style AsyncExt fill:#c8e6c9
    style BP fill:#ffccbc
    style ArrayPoolSys fill:#ffe0b2
    style TaskSys fill:#ffe0b2

SIMD Vectorized Extensions (SpanSearchSIMDExtensions.cs)

Purpose: Hardware-accelerated search using AVX2/SSE2 intrinsics (net5.0+ only)

graph LR
    subgraph "Scalar Search (Traditional)"
        T1[Check byte 1]
        T2[Check byte 2]
        T3[Check byte 3]
        T4[Check byte 4]

        T1 --> T2 --> T3 --> T4
    end

    subgraph "SIMD Search (AVX2)"
        S1[Load 32 bytes]
        S2[Compare all 32 at once]
        S3[Extract matches]

        S1 --> S2 --> S3
    end

    style T1 fill:#ffcdd2
    style T2 fill:#ffcdd2
    style T3 fill:#ffcdd2
    style T4 fill:#ffcdd2
    style S2 fill:#c8e6c9

Key Benefits:

  • 4-8x faster than scalar search for single-byte patterns
  • Processes 32 bytes at once with AVX2 (16 with SSE2)
  • Automatic hardware detection and fallback
  • Zero overhead on unsupported hardware (graceful degradation)

SIMD Capabilities:

// Hardware detection
bool IsSimdAvailable { get; }  // Checks AVX2/SSE2/Vector support
string GetSimdInfo()           // "AVX2 (256-bit SIMD, processes 32 bytes at once)"

// Single-byte searches (SIMD-optimized)
long FindFirstSIMD(ReadOnlySpan<byte> haystack, byte needle, long baseOffset = 0)
List<long> FindAllSIMD(ReadOnlySpan<byte> haystack, byte needle, long baseOffset = 0)
int CountOccurrencesSIMD(ReadOnlySpan<byte> haystack, byte needle)

// Two-byte pattern (hybrid SIMD + scalar verification)
List<long> FindAll2BytePatternSIMD(ReadOnlySpan<byte> haystack, ReadOnlySpan<byte> needle, long baseOffset = 0)

Architecture:

graph TB
    subgraph "SIMD Search Pipeline"
        Input[ReadOnlySpan byte haystack]

        subgraph "Hardware Detection"
            CheckAVX2{AVX2 Supported?}
            CheckSSE2{SSE2 Supported?}
        end

        subgraph "Vectorized Processing"
            AVX2[AVX2 Path<br/>32 bytes/iteration<br/>Vector256 byte]
            SSE2[SSE2 Path<br/>16 bytes/iteration<br/>Vector128 byte]
            Scalar[Scalar Fallback<br/>1 byte/iteration]
        end

        subgraph "Match Extraction"
            MoveMask[MoveMask Operation<br/>Extract comparison results]
            PopCount[PopCount<br/>Count set bits]
            Results[List long positions]
        end
    end

    Input --> CheckAVX2
    CheckAVX2 -->|Yes| AVX2
    CheckAVX2 -->|No| CheckSSE2
    CheckSSE2 -->|Yes| SSE2
    CheckSSE2 -->|No| Scalar

    AVX2 --> MoveMask
    SSE2 --> MoveMask
    Scalar --> Results

    MoveMask --> PopCount
    PopCount --> Results

    style AVX2 fill:#c8e6c9
    style SSE2 fill:#c8e6c9
    style Scalar fill:#fff9c4

Performance Benchmarks (Single-Byte Search): | Buffer Size | Scalar | SSE2 | AVX2 | Best Speedup | |β€”β€”β€”β€”-|——–|β€”β€”|β€”β€”|————–| | 1 KB | 45 ΞΌs | 12 ΞΌs | 8 ΞΌs | 5.6x | | 10 KB | 420 ΞΌs | 105 ΞΌs | 68 ΞΌs | 6.2x | | 1 MB | 42 ms | 11 ms | 5.2 ms | 8.1x | | 10 MB | 420 ms | 110 ms | 52 ms | 8.1x |

Hardware Requirements:

  • AVX2: Intel Haswell (2013+), AMD Excavator (2015+)
  • SSE2: Intel Pentium 4 (2001+), AMD Athlon 64 (2003+)
  • Fallback: All CPUs (scalar implementation)

Conditional Compilation:

#if NET5_0_OR_GREATER
    // SIMD intrinsics available
    using System.Runtime.Intrinsics;
    using System.Runtime.Intrinsics.X86;

    if (Avx2.IsSupported)
    {
        // Use AVX2 (32 bytes at once)
        Vector256<byte> needleVec = Vector256.Create(needle);
        Vector256<byte> chunk = Vector256.Create(haystack.Slice(pos, 32));
        Vector256<byte> matches = Avx2.CompareEqual(chunk, needleVec);
        uint mask = (uint)Avx2.MoveMask(matches);
    }
#else
    // .NET Framework 4.8: Use Vector<T> or scalar fallback
    if (Vector.IsHardwareAccelerated)
    {
        // Use Vector<byte> (platform-dependent size)
    }
#endif

When to Use SIMD:

  • βœ… Searching for single-byte values (0x00, 0xFF, etc.)
  • βœ… Counting byte occurrences
  • βœ… Processing buffers > 256 bytes
  • βœ… Running on modern CPUs (2013+)
  • ❌ Multi-byte patterns (use standard Span.IndexOf instead)
  • ❌ .NET Framework 4.8 projects (no System.Runtime.Intrinsics)

Span<byte> Extensions (ByteProviderSpanExtensions.cs)

Purpose: Zero-allocation byte operations using modern C# features

graph LR
    subgraph "Traditional Array Approach"
        A1[Allocate byte array]
        A2[Copy data to array]
        A3[Use array]
        A4[GC collects array]

        A1 --> A2 --> A3 --> A4
    end

    subgraph "Span&lt;byte&gt; Approach"
        S1[Rent from ArrayPool]
        S2[Get Span view]
        S3[Use Span]
        S4[Return to pool]

        S1 --> S2 --> S3 --> S4
    end

    style A1 fill:#ffcdd2
    style A4 fill:#ffcdd2
    style S1 fill:#c8e6c9
    style S4 fill:#c8e6c9

Key Benefits:

  • 2-5x faster operations
  • 80% reduction in GC pressure
  • 98% less memory allocation
  • Hot path optimization for performance-critical code

API Methods:

// Zero-allocation read
ReadOnlySpan<byte> GetBytesSpan(long position, int count, out byte[] buffer)

// RAII pattern with automatic cleanup
PooledBuffer GetBytesPooled(long position, int count)

// Fast equality check
bool SequenceEqualAt(long position, ReadOnlySpan<byte> pattern)

// Span-based write
int WriteBytesSpan(long position, ReadOnlySpan<byte> data)

Performance Benchmarks: | Operation | Traditional | Span<byte> | Improvement | |———–|β€”β€”β€”β€”-|β€”β€”β€”β€”β€”β€”|β€”β€”β€”β€”-| | Read 1 MB | 5.2 ms | 1.8 ms | 2.9x faster | | GC Gen 0 Collections | 120 | 15 | 8x reduction | | Memory Allocated | 50 MB | 1 MB | 98% less |

Async/Await Extensions (ByteProviderAsyncExtensions.cs)

Purpose: Non-blocking I/O operations with cancellation support

sequenceDiagram
    participant UI as UI Thread
    participant Async as Async Extension
    participant Task as Task Scheduler
    participant BP as ByteProvider
    participant FS as File System

    UI->>Async: FindAllAsync(pattern, progress, token)
    Async->>Task: Schedule background work
    Task->>BP: Search in chunks
    BP->>FS: Read file data

    loop Every 1% progress
        BP-->>Async: Report progress
        Async-->>UI: Update progress bar
    end

    alt User cancels
        UI->>Async: Cancel via token
        Async->>Task: Throw OperationCanceledException
    else Search completes
        BP-->>Async: Return results
        Async-->>UI: Return List<long>
    end

Key Benefits:

  • UI stays responsive during long operations
  • User can cancel long-running searches
  • Progress reporting for better UX
  • Scalable for large files (GB+)

API Methods:

// Async read with cancellation
Task<byte[]> GetBytesAsync(long position, int count, CancellationToken token)

// Async search with progress
Task<List<long>> FindAllAsync(byte[] pattern, long start, IProgress<int> progress, CancellationToken token)

// Async replace with progress
Task<int> ReplaceAllAsync(byte[] find, byte[] replace, long start, IProgress<int> progress, CancellationToken token)

// Async checksum calculation
Task<long> CalculateChecksumAsync(long position, long length, IProgress<int> progress, CancellationToken token)

Performance Characteristics: | File Size | Sync (UI Frozen) | Async (UI Responsive) | |———–|β€”β€”β€”β€”β€”β€”|———————–| | 10 MB | 850 ms | 850 ms (no freeze) | | 100 MB | 8.5 sec | 8.5 sec (no freeze) | | 1 GB | 85 sec | 85 sec (no freeze) |

UI Virtualization Service (VirtualizationService.cs)

Purpose: Render only visible UI elements to drastically reduce memory usage

graph TB
    subgraph "Without Virtualization"
        NV1[File: 100 MB]
        NV2[Total Lines: 6,400,000]
        NV3[Controls Created: 204,800,000]
        NV4[Memory Usage: 10.2 GB]
        NV5[Result: Out of Memory]

        NV1 --> NV2 --> NV3 --> NV4 --> NV5
    end

    subgraph "With Virtualization"
        V1[File: 100 MB]
        V2[Total Lines: 6,400,000]
        V3[Visible Lines: ~50]
        V4[Controls Created: 1,600]
        V5[Memory Usage: 35 MB]
        V6[Memory Saved: 99.7%]

        V1 --> V2 --> V3 --> V4 --> V5 --> V6
    end

    style NV5 fill:#ffcdd2
    style V6 fill:#c8e6c9

Key Benefits:

  • 80-90% memory reduction for typical files
  • 99% memory reduction for large files (100+ MB)
  • 10x faster initial rendering
  • Smooth 60fps scrolling with buffer zones

Key Classes:

public class VirtualizationService
{
    // Configuration
    int BytesPerLine { get; set; }    // Default: 16
    double LineHeight { get; set; }   // Default: 20px
    int BufferLines { get; set; }     // Default: 2 (smooth scrolling)

    // Core methods
    (long startLine, int count) CalculateVisibleRange(scrollOffset, viewportHeight, totalLines)
    List<VirtualizedLine> GetVisibleLines(scrollOffset, viewportHeight, fileLength)
    bool ShouldUpdateView(oldScroll, newScroll) // Debouncing

    // Helpers
    long EstimateMemorySavings(totalLines, visibleLines)
    string GetMemorySavingsText(totalLines, visibleLines)
    double ScrollToPosition(bytePosition, centerInView, viewportHeight)
}

public class VirtualizedLine
{
    long LineNumber { get; set; }
    long StartPosition { get; set; }
    int ByteCount { get; set; }
    double VerticalOffset { get; set; }
    bool IsBuffer { get; set; }
}

Memory Savings Examples: | File Size | Without Virtualization | With Virtualization | Savings | |———–|β€”β€”β€”β€”β€”β€”β€”β€”|β€”β€”β€”β€”β€”β€”β€”|β€”β€”β€”| | 1 MB | 320 MB RAM | 15 MB RAM | 95% | | 10 MB | 3.2 GB RAM | 25 MB RAM | 99% | | 100 MB | Out of Memory | 35 MB RAM | N/A |

Integration Architecture

graph LR
    subgraph "Developer Usage"
        App[Application Code]
    end

    subgraph "WPF HexEditor"
        HE[HexEditor Control]
        VS[VirtualizationService]
    end

    subgraph "ByteProvider Extensions (Opt-In)"
        Sync[Sync API<br/>GetByte]
        Span[Span API<br/>GetBytesPooled]
        Async[Async API<br/>GetBytesAsync]
    end

    subgraph "Core"
        BP[ByteProvider]
        FS[File System]
    end

    App -->|Uses| HE
    App -->|Optional: High-perf reads| Span
    App -->|Optional: Async searches| Async

    HE -->|Calculates viewport| VS
    HE -->|Reads data| Sync

    VS -->|Uses| BP
    Span -->|Zero-copy| BP
    Async -->|Non-blocking| BP
    Sync -->|Direct| BP

    BP --> FS

    style HE fill:#fff9c4
    style VS fill:#b3e5fc
    style Span fill:#c8e6c9
    style Async fill:#c8e6c9
    style BP fill:#ffccbc

Compatibility & Backward Compatibility

Multi-Framework Support:

<!-- .NET Framework 4.8: Span<T> via NuGet -->
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
  <PackageReference Include="System.Memory" Version="4.5.5" />
  <PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>

<!-- .NET 8.0-windows: Native Span<T> support -->
<TargetFrameworks>net48;net8.0-windows</TargetFrameworks>

Backward Compatibility:

  • βœ… All new APIs are extension methods (opt-in)
  • βœ… Existing code works unchanged
  • βœ… Zero breaking changes to public API
  • βœ… Performance improvements are transparent

Best Practices

Span<byte> Usage

// βœ… CORRECT: Use 'using' for automatic cleanup
using (var pooled = provider.GetBytesPooled(0, 1000))
{
    ReadOnlySpan<byte> data = pooled.Span;
    // Use data here
} // Buffer automatically returned to pool

// ❌ WRONG: Manual management (error-prone)
byte[] buffer;
var span = provider.GetBytesSpan(0, 1000, out buffer);
// Use span...
ArrayPool<byte>.Shared.Return(buffer); // Easy to forget!

Async/Await Usage

// βœ… CORRECT: Always use CancellationToken
private CancellationTokenSource _cts;

public async Task SearchAsync()
{
    _cts = new CancellationTokenSource();
    try
    {
        var results = await provider.FindAllAsync(pattern, 0, progress, _cts.Token);
        ProcessResults(results);
    }
    catch (OperationCanceledException)
    {
        // Expected when user cancels
    }
    finally
    {
        _cts?.Dispose();
    }
}

public void Cancel() => _cts?.Cancel();

Virtualization Usage

// βœ… CORRECT: Check if update needed (debouncing)
private double _lastScrollOffset;

void OnScroll(double newOffset)
{
    if (_virtualization.ShouldUpdateView(_lastScrollOffset, newOffset))
    {
        UpdateVisibleLines();
        _lastScrollOffset = newOffset;
    }
}

// ❌ WRONG: Update on every pixel (excessive re-renders)
void OnScroll(double newOffset)
{
    UpdateVisibleLines(); // Called 1000s of times during scroll
}

Performance Metrics Summary

Optimization Impact Use Case
SIMD (AVX2/SSE2) 4-8x faster Single-byte searches, byte counting, pattern matching
Span<byte> 2-5x faster, 90% less memory Hot paths, frequent reads, multi-byte patterns
Async/Await ∞ (UI responsive) Long searches, large file operations, user-initiated tasks
Virtualization 80-99% memory reduction Large files (> 1 MB), scrolling performance

Combined Performance (All Optimizations):

Operation: Find all occurrences of byte pattern in 10MB file
- Legacy (net48, no optimizations): 142ms, 50MB allocated, UI frozen
- Tier 1 (Span<byte> only): 48ms, 5MB allocated, UI frozen
- Tier 2 (Span + Async): 48ms, 5MB allocated, UI responsive
- Tier 3 (Span + Async + SIMD): 18ms, 5MB allocated, UI responsive
- Total improvement: 7.9x faster, 90% less memory, infinite responsiveness

Documentation

Guides

  • PERFORMANCE_GUIDE.md - Complete optimization guide (600+ lines)
    • 3-tier optimization system overview
    • When to use each optimization
    • 4 core patterns with examples
    • Migration guide from traditional to optimized
    • Real-world benchmarks
    • Best practices and troubleshooting
  • Performance README - API reference

Source Code

Testing & Benchmarks

  • WpfHexEditor.Tests - 53 unit tests (all passing)
    • SpanSearchExtensionsTests (18 tests)
    • SpanSearchSIMDTests (18 tests)
    • ByteProviderOptimizedSearchTests (17 tests)
  • WpfHexEditor.Benchmarks - BenchmarkDotNet suite
    • SpanBenchmarks - Span vs traditional array allocation
    • AsyncBenchmarks - Async performance characteristics
    • SIMDBenchmarks - AVX2/SSE2 vs scalar comparison

⚑ Performance Considerations

The architecture has been optimized for performance at multiple levels:

Service Layer Performance

  • Stateless Services: Most services are stateless for zero overhead
  • Caching Strategies: FindReplaceService implements intelligent caching (100-1000x speedup)
  • Data Structure Optimization: HighlightService uses HashSet with batching (10-100x speedup for bulk operations)
  • Lazy Loading: Services only initialize resources when needed

UI Layer Performance

  • UI Virtualization: VirtualizationService enables handling of GB-sized files
    • Only renders visible bytes (80-90% memory reduction)
    • Constant-time calculations regardless of file size
  • Rendering Cache: BaseByte/HexByte/StringByte cache expensive WPF objects (5-10x faster)
  • Width Calculation Cache: Dictionary-based O(1) lookups (10-100x faster)

Core Layer Performance

  • Span<T> APIs: Zero-allocation search operations (net5.0+)
  • SIMD Operations: Hardware-accelerated search (AVX2/SSE2)
  • Async Streaming: Non-blocking I/O for large files
  • Memory-Mapped Files: Support for files larger than RAM

Benchmarking

Performance metrics and detailed optimization guide:

Key Metrics (2026):

  • Load 1 MB file: ~80 ms
  • Load 100 MB file: ~1.5 sec
  • FindFirst (1 MB): ~12 ms
  • Search cache speedup: 460x
  • Render FPS: 45+ FPS


✨ Architecture by Derek Tremblay and contributors (2016-2026)