WPF HexEditor - Architecture Documentation
This document provides visual diagrams and detailed architecture documentation for the WPF HexEditor Control project.
π Table of Contents
- Solution Structure
- Service Layer Architecture
- Core Components Architecture
- Data Flow
- Class Relationships
- Component Dependencies
- Performance Considerations
π Related Documentation
- Performance Guide - Comprehensive performance optimization guide with benchmarking
- Main README - Project overview and quick start
- Services Documentation - Service layer details
- Benchmarks README - How to run performance benchmarks
ποΈ 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
- UI Layer can depend on Services, Core, and Infrastructure
- Services Layer can only depend on Core and Infrastructure
- Core Layer can only depend on Infrastructure
- 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
- Service-Based Architecture (2026 Refactoring)
- Extracted business logic from
HexEditorclass - 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
- Extracted business logic from
- Provider Pattern
ByteProviderabstracts file/stream access- Supports different backends (file, memory, network)
- Centralized data access point
- Command Pattern for Undo/Redo
ByteModifiedobjects represent commands- Stack-based history management
- Memory-efficient
- Caching Strategy
- 5-second TTL for search results
- Automatic invalidation on data changes
- Balances performance and correctness
- 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
- Span<byte> Extensions - Zero-allocation memory operations
- Async/Await Extensions - Non-blocking I/O operations
- SIMD Extensions - Hardware-accelerated vectorized search
- 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<byte><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<byte> 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
- SpanSearchSIMDExtensions.cs - SIMD API source (net5.0+)
- SpanSearchExtensions.cs - Span search API source
- ByteProviderSpanExtensions.cs - Span API source
- ByteProviderAsyncExtensions.cs - Async API source
- VirtualizationService.cs - Virtualization source
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:
- Performance Guide - Complete optimization documentation
- Benchmarks - Run benchmarks with BenchmarkDotNet
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
π Related Documentation
- Performance Guide - β‘ Comprehensive performance optimization guide
- Main README - Project overview
- Services Documentation - Service details
- Core Documentation - Core components
- Samples Documentation - Sample applications
β¨ Architecture by Derek Tremblay and contributors (2016-2026)