Releases: dd86k/ddhx
0.8.3
Maintenance release with some extras.
Copy-Cut-Paste Support
This release supports the copy (Alt+C), cut (Alt+X), paste (Alt+V),
and clear-clip commands!
An internal buffer is used for the clipboard, and pasting data can be
performed multiple times in a row. Without an active selection, only
the element (byte) is selected to be copied.
If more than 16 MiB of data is about to be copied, a warning will appear,
but feel free to copy as much as you want in memory. Emptying the internal
buffer can be used with the clear-clip command, useful to free the
clipboard immediately.
The actual limit of the clipboard depends on the amount of memory available
and the system's address range (on 32-bit systems, that should be 2 or 4
GiB).
No support for X11/Wayland/etc., I'd go insane. Though, you're free to
use the export-range and insert-file commands.
FIFO detection
With this update, when no filename are provided and stdin is a FIFO (or Pipe),
ddhx will automatically copy the standard input stream data into memory.
The "-" argument is no longer needed, but you can still use it.
Changelog
- Feature: FIFO/Pipe auto-detection.
- Feature: Copy-Cut-Paste support.
- Fix: Throwing an exception if stdin is a Pipe on Windows.
- Fix: Actual fix for prompts on FreeBSD framebuffer outside tmux.
- Misc: Ask to overwrite file for
export-rangecommand. - Misc: Made save prompt easier to parse for its default action (
(Y/N)to[y/N]).
0.8.2
Maintenance release with a few extras.
Changelog
- Improved: Search operations can be cancelled using Ctrl+C or Escape.
- Improved: Search operations will show a progress bar after some time.
- Fixed: Prompt line wrapping on Windows.
- Fixed: Misrendering data after cursor on Windows.
- Fixed: Unusable prompt in FreeBSD fbcon.
- Deprecated:
autoresizeconfiguration, usecolumns autoinstead. - Removed: "chunk" backend.
musl+static built against Alpine 3.22 x86_64.
0.8.1
0.8
This release features a new undo-redo system, a readline implementation, and some quality of life changes.
Happy holidays!
New Undo-Redo Stack
The Piece V1 backend had a serious design flaw.
After extremely long sessions, performing thousands of edits, the memory
usage would steadily increase towards hundreds of megabytes.
To fix this, a new Piece V2 "piecev2" (new default) was introduced.
In the same scenario, it would instead only use about 2.5 MiB of
additional GC memory used.
Technical background
This new backend replaces the snapshot mechanic to a command history stack,
trading undo and redo operations from O(1) to O(n). However, saves a tremendous
amount of memory (only saving critical pieces vs. saving whole snapshots).
When I wrote the first version of the Piece Table, I wanted something that
worked ASAP. I knew the limitations, but I still wanted to release something
sooner!
I have written a
blog post
about the new undo-redo stack, if you want a more technical analysis.
Readline
Up until now, the only way to cancel input on a prompt is to give it an empty line.
Not very intiutive, so, I thought of capturing Ctrl+C (^C).
The nature of terminal input has lead me to implement my own variant of a readline,
which allows, for example, word navigation (Ctrl+Left/Right arrows) and word deletion
(Ctrl+Backspace), alongside support for handling Ctrl+C both in prompt and outside.
This also opens the door to support history, input selection, and more. Eventually.
It is far from perfect, but still an improvement from seeing a sabotaged terminal
session when ^C was hit, or seeing control characters in the input.
Right now, ^C is hard-coded to a few things:
- Quit the editor if hit twice. A warning is displayed after the first invokation;
- Cancel the current selection;
- And cancel the active prompt.
Design-wise, "cancel anything" is my best bet when handling ^C.
To celebrate the new Readline feature, and to be consistent with other text editors,
the prompt was moved at the bottom of the screen.
Technical background
Technically, each console or terminal emulator handle their own line input buffer.
By default, it's only ever returned to the application (to "you") when Enter or Return
are hit.
On POSIX, to capture ^C from read(2), you need to disable ISIG (lflags). However, just
disabling ISIG puts ^C (\033) into the line buffer. To get the key input immediately,
ICANON (lflags), the terminal's line mode, needs to be disabled as well.
On Windows, to capture ^C from ReadConsoleInput, you disable ENABLE_PROCESSED_INPUT.
Trying to manage these on the fly is rather painful. Eventually, I'll need to do multithread
on on the front-end to come up with a solution to cancel current operations (ie, search).
Length-only Operations
New length-only shorthand for Range expressions.
Instead of .:+3 (current position plus 3 additional elements), 4 acts as the same.
Notable examples (without a selection):
- Insert 1,000 zeros at cursor position:
insert 1000 x:0 - Fill the next 2 bytes with this integer:
replace 2 0x1234 - Select the next 128 bytes:
select 128
Technical background
Technically, ranges are inclusive. However, this update allows omitting the :
character to denote a length "range" instead. It's an implicit behavior that
transform LEN into .:+LEN-1 internally.
Changelog
- Featured: New Piece V2 "piecev2" backend.
- Featured: New readline module.
- Featured: Length-only range expressions.
- Added: "mark" and "unmark" commands (start/end a selection). Alternative to Shift+Nav.
- Added: "header" and "status" configurations as visibility toggles.
- Added: "mirror-cursor" configuration. This makes the cursor visible in both columns, in a different color.
- Improved: Make "menu" command (command prompt) bindable.
- Improved: Fix cases where special characters, like ':', couldn't be used for binding.
- Improved: Bindings now accept modifiers in any given order.
- Fixed: Undo/redo with a selection did not unselect it.
- Fixed: Minor improvements to rendering when changing column sizes.
- Removed:
--help-debug. Its content (target triple, compiler used) were moved into--version.
0.7.2
0.7.1
Bugfixes and small quality of life changes.
Range Expression
A new expression was added to denote ranges: Range Expressions.
Range expressions are inclusive in an attempt to avoid confusion when
performing position-sensitive operations.
Implemented for "insert", "replace", "select", and "delete" commands.
Warnings:
- "select" now takes one argument (Range Expression) instead of two separate positions.
- "insert" and "replace" still only requires pattern arguments when using a visual selection.
- "insert" and "replace" require a range as first argument when no visual selection is active.
Special characters:
- '$': End of document.
- '.': Cursor position.
Examples:
- "0x10:0x12" selects positions 0x10, 0x11, and 0x12 (3 Bytes).
- "0x10:+2" selects the same positions from the previous examples.
- "64:$" selects positions starting from 64 to the end of document (EOF).
- ".:0x200" selects positions starting from cursor position to 0x200.
- "0x200:." selects positions starting from 0x200 to cursor position.
- ".:$" selects positions starting from cursor position to end of document (EOF).
- ".:+3" selects positions starting from cursor plus 3 next Bytes.
Command examples:
- "replace 0x430:0x433 x:00" zero-fills bytes 0x430 to 0x433 (4 Bytes).
- "delete .:+0xFF" deletes 256 bytes starting from cursor.
- "insert 100:+99 s:Hello" inserts pattern at specified range.
- "insert .:+999999 x:0" can zero-fill an empty document for 1 million bytes.
Changelogs
- Added: Range expressions for "replace", "insert", "select", and "delete" commands.
- Fixed: "replace" and "insert" commands taking only one pattern parameter.
- Fixed: Crash when "replace" and "insert" commands were missing parameters.
- Fixed: Missing writing mode checks when in restricted (read-only) mode.
- Improved: Add message when a search starts.
- Improved: goto % now accepts decimals (e.g., "goto %5.86").
- Improved: Make more argument prompts cancellable by giving empty input.
- Improved: "change-mode" accepts a writing mode argument.
- Default (without arguments) remains switching between Insert/Overwrite.
- Arguments: "i"=Insert, "o"=Overwrite, "r"=Restricted (Read-only).
- It is now possible to enable restricted mode at runtime.
- Beware, you cannot switch from restricted mode once set!
- You can undo and redo after setting restricted mode, but cannot save.
0.7
Welcome, welcome to ddhx 0.7.
Brand new backend, performance optimization, and general improvements.
New Backend
A new backend was introduced for this update.
The previous backend served its purpose, but without going too much into
technical details, it was too inflexible to allow new operations to be
implemented without significant effort (and hair loss).
So, a new Piece Table backend was added, enabling efficient insertion and deletion
operations that were previously impractical.
To demonstrate its capabilities, new commands were added:
replace/insert- add data from lazy patternsreplace-file/insert-file- add data from files as references
For example, you can select a large region and invoke
replace s:HELLO!! (using the pattern system) to fill the entire selection,
using a minimal amount of memory.
Another example? Select and delete those pesky iTXt PNG chunks!
Want to insert Windows11.vdi file at the current position? insert-file Windows11.vdi
The old backend remains available via the DDHX_BACKEND=chunk environment variable,
though it lacks all the new features and will be removed in a future release.
I've also written a brief blog article about the technical details of the new backend.
Note
Memory usage is a little bad with very lengthy sessions.
While 100 edits will use about 1.16 MiB of additional memory,
1,000 edits will use about ~93.5 MiB of memory.
This will be fixed in a later release.
Optimizations
Did ddhx feel a little slow, especially on Windows?
- Buffered rendering: The headerbar and main view are now buffered.
- 10× speedup on Windows (conhost: ~50-55 ms → ~5 ms)
- 5× speedup on Linux (VTE: 2-3 ms → ~500 µs)
- General gains across all platforms
- Smarter document reads: The editor avoids redundant reads when possible.
- ~500 µs saved when viewing without edits
- Improved save strategy: Files are saved using atomic rename operations.
- Previously: wrote to temp dir, then copied to destination (2 writes)
- Now: write to temp file in target dir, then atomic rename (1 write + rename)
Changelog
Featured:
- New "piece" backend with efficient insertion and deletion support.
Added:
- Development environment variables:
DDHX_LOGfor debug logging.DDHX_BACKENDfor backend selection: "piece" (default) or "chunk".DDHX_CHUNKSIZEfor chunk backend buffer size (deprecated).
- Commands:
report-version- display ddhx version information.export-range- export selection to a new file.select-all(Ctrl+A) - select entire document.select-top(Ctrl+Shift+Home) - select from current position to start.select-bottom(Ctrl+Shift+End) - select from current position to end.select- explicitly select a range using START and END parameters.delete(Delete) anddelete-back(Backspace) - remove bytes.insert/replace- add data using patterns.insert-file/replace-file- add data from files.
Improved:
- Rendering performance through output buffering.
- Document read efficiency, only read when necessary for updates.
- Save reliability using atomic file operations.
- Display optimization (truncate empty rows).
- Save prompt clarity:
"Save? (Yes/No/Cancel) ". - Patterns: Now supports multibyte values, so
0x1234will be two bytes (platform-native). - System path is now checked for the configuration file.
- The home path is now prioritized over the application path for the configuration file.
- Renamed
--help-configsto--help-config, now displays which config is used.
Removed:
--logand--chunksizecommand-line options (use environment variables instead).
0.7 Beta 1
Welcome to the ddhx 0.7 beta!
This release introduces a completely rewritten backend.
With such fundamental changes, bugs are expected. Happy Halloween!
What's New
- New piece table backend: Uses a Red-Black Tree indexing for efficient editing with insert/delete support
- Pattern insertion/replacement: Insert repeating byte patterns without allocating memory
- File insertion/replacement: Insert entire files as references
- Improved undo/redo: Snapshot-based history
How to Help Testing
Enable Logging
Set the DDHX_LOG environment variable to capture debug information:
- Windows:
SET DDHX_LOG=ddhx.log(or environment variable dialog) - POSIX:
export DDHX_LOG=ddhx.log(or full path to your liking)
When reporting bugs, please include this log file!
What to Test
Focus on edge cases and boundary conditions, especially past 4 GiB.
Basic operations:
- Replace, insert, and delete data at:
- Start of file (position 0)
- End of file
- Odd offsets (e.g., 0x1337, middle of the view)
- Across piece boundaries (after previous edits)
Pattern operations:
insert PATTERNandreplace PATTERN(with selection)- Example: Select bytes, then
replace x:01 02 03 04 - "Cut into" new patterns by overwriting partially
- Replace single bytes within inserted patterns
File operations:
insert-file PATHandreplace-file PATH- Insert/replace at various positions
- Undo/redo after file operations
- Edit around inserted file data
Stress testing:
- Multiple sequential edits
- Deep undo/redo chains
- Large selections
- Mixed operation types (insert → replace → delete → undo)
Reporting Bugs
Please report issues with:
- Log file (DDHX_LOG output)
- Steps to reproduce
- Expected vs actual behavior
0.6
This release focuses on quality of life improvements.
Featured changes include a search command, selection capabilities, and
configuration file support.
Warning
quit has been remapped from Q to Ctrl+Q as a default key, to really confirm quitting as an action (you can still bind it back to Q).
Find Data
What's a hex editor without a search function?
You're now able to search by pattern. A pattern is made up of a prefix and data.
For example, x:00 01 will search for [0x00, 0x01].
It is possible to compound patterns: find o:33 s:hi x:dd 00 will search for
[0x1b, 0x68, 0x69, 0xdd, 0x00].
Syntax: find PATTERN...
Patterns:
x:,0x: 8-bit unsigned hexadecimal.d:: 8-bit unsigned decimal.o:: 8-bit unsigned octal.s:,"...": UTF-8 string.- Quotes have to be escaped (
\"...\") due to prompt processing. - Future update: Transcode to current character set.
- Quotes have to be escaped (
Commands implemented:
find(Ctrl+F),find-back(Ctrl+B)find-backsimply initiates the search backwards, meaning towards start of document.
find-next(Ctrl+N),find-prev(Shift+N)- Allows traversing results forward and backward.
Selecting Data
Selection has to be my new favourite feature for this version.
Using the Shift modifier and navigation keys, it's possible to select data and
perform shortcuts and commands directly with it.
Example flow (skip forward):
- Select bytes, this will be used as a search needle.
- Either hit Ctrl+RightArrow or use command
skip-front.
That's it! The command will automatically detect if there is a selection and
use it.
Commands supporting selection:
move-skip-front,move-skip-back- Selection allows skipping data by this pattern.
- e.g., Selecting [0x01, 0x02, 0x03, 0x04] will skip all of this pattern or to EOF, like a word.
find,find-back- Selection allows searching for this same needle by selected pattern.
- e.g., Selecting [0x01, 0x02, 0x03, 0x04] will search for the same instance.
goto- Selection allows performing an absolute seek to this position.
- e.g., Selecting [0x01, 0x02, 0x03, 0x04] will go to this position (0x04030201 on little systems).
Commands implemented:
select-left(Shift+LeftArrow)select-right(Shift+RightArrow)select-up(Shift+UpArrow)select-down(Shift+DownArrow)select-home(Shift+Home)select-end(Shift+End)
Bind Key
Prefer your current code editor keybinds?
The bind command allows you to dynamically rebind keys.
Example of binding keys to match vim navigation keys:
bind h leftbind j downbind k upbind l right
It's also possible to map new default parameters:
bind shift+p goto +32will map Shift+P togoto +32(jump 32 bytes ahead).
Keys set within a session are only valid for the session.
The unbind command either unbinds the key or resets it to its default keymap.
Syntax:
bind KEY COMMAND [PARAMETERS...]unbind KEYreset-keys
Config File
If you want EBCDIC by default, you can!
Configuration files are now supported.
It's now possible to set default configuration values and remap keys.
Example format:
# Comments uses '#'
# For "address", "d", "dec", and "decimal" are all valid!
address decimal
columns 10
# Imitate vim navigation
bind h left
bind j down
bind k up
bind l right
# Add command parameters to command!
bind ctrl+p goto +32
# Older quit behavior
bind q quit
# Imitate nano
bind ctrl+x quit
bind ctrl+g find-next
Here are example paths that will be tried, in order. Only the first config path
found is loaded. Command-line parameters still take priority.
Windows:
%LOCALAPPDATA%\ddhx\.ddhxrc- e.g.,
C:\Users\dd\AppData\Local\ddhx\.ddhxrc
- e.g.,
%USERPROFILE%\.ddhxrc- e.g.,
C:\Users\dd\.ddhxrc
- e.g.,
POSIX:
$XDG_HOME_CONFIG/ddhx/.ddhxrc- e.g.,
/home/dd/.config/ddhx/.ddhxrc
- e.g.,
~/.ddhxrc- e.g.,
/home/dd/.ddhxrc
- e.g.,
To avoid loading configs, use -I or --norc.
To use a specific config file, use -f or --rcfile with a specified file.
Changelog
- Added: Config file support.
- Added:
select-right(Shift+RightArrow),select-left(Shift+LeftArrow),select-up(Shift+UpArrow),select-down(Shift+DownArrow),select-home(Shift+Home),select+end(Shift+End) selection commands. - Added:
find(Ctrl+F),find-back(Ctrl+B),find-next(Ctrl+N),find-prev(Shift+N) search commands. - Added:
bindcommand. - Added:
unbindcommand. - Added:
reset-keyscommand. - Added:
autoresizeconfiguration. - Improved: VT220 input scanning, and other general improvements to terminal handling.
- Improved: Command prompt handles embedded quote escapes.
- Removed: "start" and "end" aliases for
goto, since we havetop(Ctrl+Home) andbottom(Ctrl+End) commands.
0.5.1
This release fixes cursor-skip-front and cursor-skip-back. No idea how I did not notice that while testing.
Changelog since 0.5:
- Added:
--help-commands,--help-configs, and--help-debug. - Changed
--addressto--addressingand-A.--addressing=oand--addressing=octalmeans the same thing.-Aoand-Aoctalmeans the same thing.
- Fixed:
cursor-skip-back(Ctrl+LeftArrow) andcursor-skip-front(Ctrl+RightArrow).