yaxpeax-arch-0.3.2/.cargo_vcs_info.json 0000644 00000000136 00000000001 0013430 0 ustar {
"git": {
"sha1": "a79de0575f95fff6f031df8e30703045bbb0037b"
},
"path_in_vcs": ""
} yaxpeax-arch-0.3.2/.gitignore 0000644 0000000 0000000 00000000024 10461020230 0014204 0 ustar 0000000 0000000 /target/
Cargo.lock
yaxpeax-arch-0.3.2/CHANGELOG 0000644 0000000 0000000 00000017457 10461020230 0013450 0 ustar 0000000 0000000 ## TODO
~~TODO: Reader::next_n should return the number of items read as Err(ReadError::Incomplete(n)) if the buffer is exhausted~~
* a reader's `.offset()` should reflect the amount of items that were consumed, if any. if a reader can quickly determine
there is not enough input, should it return Incomplete(0) or ExhaustedInput? Incomplete(0) vs ExhaustedInput may still
imply that some state was changed (an access mode, for example). this needs more thought.
TODO: Reader::offset should return an AddressDiff
, not a bare Address
* quick look seems reasonable enough, should be changed in concert with
yaxpeax-core though and that's more than i'm signing up for today
TODO: impls of `fn one` and `fn zero` so downstream users don't have to import num_traits directly
* seems nice at first but this means that there are conflicting functions when Zero or One are in scope
... assuming that the idea at the time was to add `fn one` and `fn zero` to `AddressBase`.
TODO: 0.4.0 or later:
* remove `mod colors`, crossterm dependency, related feature flags
## 0.3.2
fix yaxpeax-arch not building for non-x86 targets when alloc is not enabled
## 0.3.1
fix InstructionTextSink::write_char to not panic in debug builds
## 0.3.0
added a new crate feature flag, `alloc`.
this flag is for any features that do not require std, but do require
containers from `liballoc`. good examples are `alloc::string::String` or
`alloc::vec::Vec`.
added `yaxpeax_arch::display::DisplaySink` after revisiting output colorization.
`DisplaySink` is better suited for general markup, rather than being focused
specifically on ANSI/console text coloring. `YaxColors` also simply does not
style text in some unfortunate circumstances, such as when the console that
needs to be styled is only written to after intermediate buffering.
`DisplaySink` also includes specializable functions for writing text to an
output, and the implementation for `alloc::string::String` takes advantage of
this: writing through `impl DisplaySink for String` will often be substantially
more performant than writing through `fmt::Write`.
added `mod color_new`:
this includes an alternate vision for `YaxColors` and better fits with the
new `DisplaySink` machinery; ANSI-style text markup can be done through the
new `yaxpeax_arch::color_new::ansi::AnsiDisplaySink`.
this provides more flexibility than i'd initially expected! yours truly will
be using this to render instructions with HTML spans (rather than ANSI
sequences) to colorize dis.yaxpeax.net.
in the future, `mod colored` will be removed, `mod color_new` will be renamed
to `mod color`.
deprecated `mod colored`:
generally, colorization of text is a presentation issue; `trait Colorize`
mixed formatting of data to text with how that text is presented, but that is
at odds with the same text being presented in different ways for which
colorization is not generic. for example, rendering an instruction as marked
up HTML involves coloring in an entirely different way than rendering an
instruction with ANSI sequences for a VT100-like terminal.
added `yaxpeax_arch::safer_unchecked` to aid in testing use of unchecked methods
these were originally added to improve yaxpeax-x86 testing:
https://github.com/iximeow/yaxpeax-x86/pull/17, but are being pulled into
yaxpeax-arch as they're generally applicable and overall wonderful tools.
thank you again 522!
added `mod testkit`:
this module contains tools to validate the correctness of crates implementing
`yaxpeax-arch` traits. these initial tools are focused on validating the
correctness of functions that write to `DisplaySink`, especially that span
management is correct.
`yaxpeax-x86`, for example, will imminently have fuzz targets to use these
types for its own validation.
made VecSink's `records` private. instead of extracting records from the struct
by accessing this field directly, call `VecSink::into_inner()`.
made VecSink is now available through the `alloc` feature flag as well as `std`.
meta: the major omission in this release is an architecture-agnostic way to
format an instruction into a `DisplaySink`. i haven't been able to figure out
quite the right shape for that! it is fully expected in the future, and will
probably end up somehow referenced through `yaxpeax_arch::Arch`.
## 0.2.8
added an impl of `From` for `StandardPartialDecoderError`, matching the existing `StandardDecodeError` impl.
moved a use of `serde` types to be covered by the relevant cfg flag; using `colors` without `serde` (unlikely) now actually builds.
fixed up doc comments to build without error.
(and additional testing permutations to validate cfg flags and doc comments in the future)
## 0.2.7
moved `AnnotatingDecoder` and its associated types to `annotation/`, for module-level documentation about that feature.
yanked 0.2.6 because there was not yet a user of it other than myself, and it had this feature in the wrong location in the crate.
## 0.2.6
added `AnnotatingDecoder` and associated traits `FieldDescription` and `DescriptionSink` for architectures to report meanings for bit ranges in decoded instructions.
added `NullSink`, with an `impl DescriptionSink for NullSink` - `NullSink` can always be used to discard instruction annotations. this is mostly useful for shared annotating and non-annotating decode logic.
added a `docs/` directory for `yaxpeax-arch`: trip reports for `yaxpeax-arch` design. if `yaxpeax` eventually grows an RFC process one day, these are the kind of changes that would get RFC'd.
added `docs/0001-AnnotatingDecoder.md`, describing motivation and implementation notes of `AnnotatingDecoder`.
## 0.2.5
added `yaxpeax-lc87` to the matrix
## 0.2.4
fix incorrect `Reader` impls of `offset` and `total_offset` on non-`u8` words
## 0.2.3
added `Reader` impls for `U8Reader` on `u16` addresses
## 0.2.2
added `ReaderBuilder` trait and impls for `U8Reader` on various address and word types.
added documentation for `Reader`, `U8Reader`, and `ReaderBuilder`.
avoid an unlikely violation of `core::ptr::offset` safety rules on 32-bit architectures.
## 0.2.1
updated architecture matrix
## 0.2.0
correct a bug in 0.1.0 that incorrectly bounded `DecodeError` and did not actually require `std::error::Error`. added a test that `std::error::Error` is actually required of `Arch::DecodeError` in non-std builds.
## 0.1.0
new trait `Reader` to provide a reader of `Arch`-defined `Word`s. in many cases it is acceptable for `Word` to be `u8`, but `yaxpeax-arch` provides pre-defined words `u8`, `U16le`, `U16be`, `U32le`, `U32be`, `U64le`, and `U64be`.
`yaxpeax_arch::U8Reader` is a struct to read from `&[u8]` that implements `Reader` for all predefined words. it is suitable to read larger words if the minimum word size is still one byte.
`Decoder` now decodes from a `Reader`, to prepare for ISAs where instruction sizes are not multiples of 8 bits.
`yaxpeax_arch::DecodeError` now requires a `std::error::Error` impl for `std` builds, to support interop with the Rust `error` ecosystem.
committed to `AddressDiff` being convertable to a primitive with `AddressDiff::to_const`
- this addresses the need for hacks to translate an instruction length into a usize
## 0.0.5
swap the `termion` dependency for `crossterm`. this is motivated by improved cross-platform support (notably Windows) as well as removing a type parameter from `Colored` and `YaxColors`.
## 0.0.4
add `AddressDiff`. `LengthedInstruction::len` now return `AddressDiff`. the length of an instruction is the difference between two addresses, not itself an address.
## 0.0.3
`ColorSettings` gets a default impl
## 0.0.2
add `AddressDisplay` to provide a usable interface to display `Address` implementors.
at the same time, remove `Address::stringy()`. it was a very bad interface, and will not be missed.
## 0.0.1
history starts here
yaxpeax-arch-0.3.2/Cargo.toml 0000644 00000002603 00000000001 0011427 0 ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "yaxpeax-arch"
version = "0.3.2"
authors = ["iximeow "]
description = "fundamental traits to describe an architecture in the yaxpeax project"
readme = "README.md"
keywords = [
"disassembly",
"disassembler",
]
license = "0BSD"
repository = "https://git.iximeow.net/yaxpeax-arch/"
[profile.release]
lto = true
[dependencies.crossterm]
version = "0.27.0"
optional = true
[dependencies.num-traits]
version = "0.2"
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dev-dependencies.anyhow]
version = "1.0.41"
[dev-dependencies.thiserror]
version = "1.0.26"
[features]
address-parse = []
alloc = []
color-new = []
colors = ["crossterm"]
default = [
"std",
"alloc",
"use-serde",
"color-new",
"address-parse",
]
std = ["alloc"]
use-serde = [
"serde",
"serde_derive",
]
yaxpeax-arch-0.3.2/Cargo.toml.orig 0000644 0000000 0000000 00000002407 10461020230 0015112 0 ustar 0000000 0000000 [package]
authors = [ "iximeow " ]
description = "fundamental traits to describe an architecture in the yaxpeax project"
edition = "2021"
keywords = ["disassembly", "disassembler"]
license = "0BSD"
name = "yaxpeax-arch"
repository = "https://git.iximeow.net/yaxpeax-arch/"
version = "0.3.2"
[dependencies]
"num-traits" = { version = "0.2", default-features = false }
"crossterm" = { version = "0.27.0", optional = true }
"serde" = { version = "1.0", optional = true }
"serde_derive" = { version = "1.0", optional = true }
[dev-dependencies]
anyhow = "1.0.41"
thiserror = "1.0.26"
[profile.release]
lto = true
[features]
default = ["std", "alloc", "use-serde", "color-new", "address-parse"]
std = ["alloc"]
alloc = []
# enables the (optional) use of Serde for bounds on
# Arch and Arch::Address
use-serde = ["serde", "serde_derive"]
# feature flag for the existing but misfeature'd initial support for output
# coloring. the module this gates will be removed in 0.4.0, which includes
# removing `trait Colorize`, and requires a major version bump for any
# dependency that moves forward.
colors = ["crossterm"]
# feature flag for revised output colorizing support, which will replace the
# existing `colors` feature in 0.4.0.
color-new = []
address-parse = []
yaxpeax-arch-0.3.2/LICENSE 0000644 0000000 0000000 00000001173 10461020230 0013227 0 ustar 0000000 0000000 Copyright (c) 2020 iximeow
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
yaxpeax-arch-0.3.2/Makefile 0000644 0000000 0000000 00000001142 10461020230 0013656 0 ustar 0000000 0000000 test: build-smoketest test-std test-no-std test-serde-no-std test-colors-no-std test-color-new-no-std test-alloc-no-std
build-smoketest:
cargo build
cargo build --no-default-features
cargo build --no-default-features --target wasm32-wasi
test-std:
cargo test
test-no-std:
cargo test --no-default-features
test-serde-no-std:
cargo test --no-default-features --features "serde"
test-colors-no-std:
cargo test --no-default-features --features "colors"
test-color-new-no-std:
cargo test --no-default-features --features "color-new"
test-alloc-no-std:
cargo test --no-default-features --features "alloc"
yaxpeax-arch-0.3.2/README.md 0000644 0000000 0000000 00000015461 10461020230 0013506 0 ustar 0000000 0000000 ## yaxpeax-arch
[](https://crates.io/crates/yaxpeax-arch)
[](https://docs.rs/yaxpeax-arch)
shared traits for architecture definitions, instruction decoders, and related interfaces for instruction decoders from the yaxpeax project.
typically this crate is only interesting if you're writing code to operate on multiple architectures that all implement `yaxpeax-arch` traits. for example, [yaxpeax-dis](https://crates.io/crates/yaxpeax-dis) implements disassembly and display logic generic over the traits defined here, so adding a new decoder is usually only a one or two line addition.
`yaxpeax-arch` has several crate features, which implementers are encouraged to also support:
* `std`: opt-in for `std`-specific support - in this crate, `std` enables a [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) requirement on `DecodeError`, allowing users to `?`-unwrap decode results.
* `color_new`: enables traits and structs to stylize formatted instructions, including ANSI colorization.
* ~`colors`~: DEPRECATED. enables (optional) [`crossterm`](https://docs.rs/crossterm/latest/crossterm/)-based ANSI colorization. default coloring rules are defined by [`ColorSettings`](https://docs.rs/yaxpeax-arch/latest/yaxpeax_arch/struct.ColorSettings.html), when enabled.
* `address-parse`: enable a requirement that `yaxpeax_arch::Address` be parsable from `&str`. this is useful for use cases that, for example, read addresses from humans.
* `use-serde`: enable [`serde`](https://docs.rs/serde/latest/serde/) serialization and deserialization bounds for types like `Address`.
with all features disabled, `yaxpeax-arch`'s only direct dependency is `num-traits`, and is suitable for `#![no_std]` usage.
### design
`yaxpeax-arch` has backwards-incompatible changes from time to time, but there's not much to make incompatible. the main benefit of this crate is the [`Arch`](https://docs.rs/yaxpeax-arch/latest/yaxpeax_arch/trait.Arch.html) trait, for other libraries to build architecture-agnostic functionality.
nontrivial additions to `yaxpeax-arch` should include some discussion summarized by an addition to the crate [`docs/`](https://github.com/iximeow/yaxpeax-arch/tree/no-gods-no-/docs). you may ask, "where does discussion happen?", and the answer currently is in my (iximeow's) head, or various discord/irc/discord/email conversations. if there's need in the future, `yaxpeax` may develop a more consistent process.
`yaxpeax-arch` intends to support ad-hoc development of architecture support. maintainers of various architectures' crates may not want to implement all available interfaces to a complete level of detail, and must not be required to. incomplete implementations may be an issue for downstream users, but library quality is mediated by human conversation, not `yaxpeax-arch` interfaces. extensions to these fundamental definitions should be considerate of partial and incomplete implementations.
### implementations
there are numerous architectures for which decoders are implemented, at varying levels of completion. now and in the future, they will be enumerated here:
| symbol | meaning |
| ------ | ------- |
| 🥳 | complete, reliable |
| ⚠️| "complete", likely has gaps |
| 🚧 | incomplete |
| ❓ | unimplemented |
| architecture | library | decode | tests | benchmarks | note |
| ------------ | ------- | ------ | ----- | ---------- | ---- |
| `x86_64` | [yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-x86) | 🥳 | 🥳 | 🥳 | |
| `x86:32` | [yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-x86) | 🥳 | 🥳 | ❓ | sse and sse2 support cannot be disabled |
| `x86:16` | [yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-x86) | 🥳 | 🥳 | ❓ | instructions above the 8086 or 286 cannot be disabled |
| `ia64` | [yaxpeax-ia64](https://www.github.com/iximeow/yaxpeax-ia64) | 🥳 | ⚠️ | ❓ | lack of a good oracle has complicated testing |
| `armv7` | [yaxpeax-arm](https://www.github.com/iximeow/yaxpeax-arm) | 🚧 | 🚧 | ❓ | NEON is not yet supported |
| `armv8` | [yaxpeax-arm](https://www.github.com/iximeow/yaxpeax-arm) | 🚧 | 🚧 | ❓ | a32 decoding is not yet supported, NEON is not supported |
| `m16c` | [yaxpeax-m16c](https://www.github.com/iximeow/yaxpeax-m16c) | ⚠️ | 🚧 | ❓ | |
| `mips` | [yaxpeax-mips](https://www.github.com/iximeow/yaxpeax-mips) | 🚧 | 🚧 | ❓ | |
| `msp430` | [yaxpeax-msp430](https://www.github.com/iximeow/yaxpeax-msp430) | 🚧 | 🚧 | ❓ | |
| `pic17` | [yaxpeax-pic17](https://www.github.com/iximeow/yaxpeax-pic17) | 🚧 | 🚧 | ❓ | |
| `pic18` | [yaxpeax-pic18](https://www.github.com/iximeow/yaxpeax-pic18) | 🚧 | 🚧 | ❓ | |
| `pic24` | [yaxpeax-pic24](https://www.github.com/iximeow/yaxpeax-pic24) | ❓ | ❓ | ❓ | exists, but only decodes `NOP` |
| `sm83` | [yaxpeax-sm83](https://www.github.com/iximeow/yaxpeax-sm83) | 🥳 | 🚧 | ❓ | |
| `avr` | [yaxpeax-avr](https://github.com/The6P4C/yaxpeax-avr) | 🥳 | 🚧 | ❓ | contributed by [@the6p4c](https://twitter.com/The6P4C)! |
| `sh`/`sh2`/`j2`/`sh3`/`sh4` | [yaxpeax-superh](https://git.sr.ht/~nabijaczleweli/yaxpeax-superh) | 🥳 | 🚧 | ❓ | contributed by [наб](https://nabijaczleweli.xyz) |
| `MOS 6502` | [yaxpeax-6502](https://github.com/cr1901/yaxpeax-6502) | ⚠️ | ❓ | ❓ | contributed by [@cr1901](https://www.twitter.com/cr1901) |
| `lc87` | [yaxpeax-lc87](https://www.github.com/iximeow/yaxpeax-lc87) | 🥳 | ⚠️ | ❓ | |
#### feature support
`yaxpeax-arch` defines a few typically-optional features that decoders can also implement, in addition to simple `(bytes) -> instruction` decoding. these are `yaxpeax-arch` traits (or collections thereof) which architectures implement, not crate features.
`description_spans`: implementation of [`AnnotatingDecoder`](https://docs.rs/yaxpeax-arch/latest/yaxpeax_arch/trait.AnnotatingDecoder.html), to decode instructions with bit-level details of what incoming bitstreams mean.
`contextualize`: implementation of [`ShowContextual`](https://docs.rs/yaxpeax-arch/latest/yaxpeax_arch/trait.ShowContextual.html), to display instructions with user-defined information in place of default instruction data. typically expected to show label names instead of relative branch addresses. **i do not recommend implementing this trait**, it needs significant reconsideration.
| architecture | `description_spans` | `contextualize` |
| ------------ | ------------------- | --------------- |
| `x86_64` | 🥳 | ❓ |
| `ia64` | ⚠️ | ❓ |
| `msp430` | 🥳 | ❓ |
### mirrors
the canonical copy of `yaxpeax-arch` is at [https://git.iximeow.net/yaxpeax-arch](https://git.iximeow.net/yaxpeax-arch).
`yaxpeax-arch` is also mirrored on GitHub at [https://www.github.com/iximeow/yaxpeax-arch](https://www.github.com/iximeow/yaxpeax-arch).
yaxpeax-arch-0.3.2/docs/0001-AnnotatingDecoder.md 0000644 0000000 0000000 00000015741 10461020230 0017450 0 ustar 0000000 0000000 ## `DescriptionSink`
most architectures' machine code packs interesting meanings into specific bit fields, and one of the more important tasks of the yaxpeax decoders is to unpack these into opcodes, operands, and other instruction data for later use. in the worst case, some architectures - typically interpreted bytecodes - do less bit-packing and simply map bytes to instructions.
the yaxpeax decoders' primary role is to handle this unpacking into user code-friendly structs. i want decoders to be able to report the meaning of bitfields too, so user code can mark up bit streams.
implementing this capability should (borderline-"must") not regress performance for decoders that do not use it. as a constraint, this is surprisingly restrictive!
a. it rules out a parameter to [`Decoder::decode_into`](https://docs.rs/yaxpeax-arch/0.2.5/yaxpeax_arch/trait.Decoder.html#tymethod.decode_into): an ignored or unused parameter can still change how `decode_into` inlines.
b. it rules out extra state on `Decoder` impls: writing to an unread `Vec` is still extra work at decode time.
decoders other than x86 are less performance-sensitive, so **light** regressions in performance may be tolerable.
i would also like to:
c. not require decoders implement this to participate in code analysis [`yaxpeax-core`](https://github.com/iximeow/yaxpeax-core/) provides.
d. re-use existing decode logic -- requiring myself and other decoder authors to write everything twice would be miserable.
the point `c` suggests not adding this capability to existing traits. taken together, these constraints point towards a _new_ trait that _could_ be implemented as an independent copy of decode logic, like:
```rust
trait AnnotatingDecoder {
fn decode_with_annotation<
T: Reader,
>(&mut self, inst: &mut A::Instruction, words: &mut T) -> Result<(), A::DecodeError>;
}
```
but for implementations, it's easiest to tack this onto an existing `Arch`'s `InstDecoder`. point `b` means no new state, so wherever details about a span of bits are recorded, it should be an additional `&mut` parameter. then, if that parameter is an impl of some `Sink` trait, `yaxpeax_arch` can provide a no-op implementation of the `Sink` and let call sites be eliminated for non-annotating decodes.
taken together, this ends up adding three traits:
```rust
pub trait DescriptionSink {
fn record(&mut self, start: u32, end: u32, description: Descriptor);
}
pub trait FieldDescription {
fn id(&self) -> u32;
}
pub trait AnnotatingDecoder {
type FieldDescription: FieldDescription + Clone + Display + PartialEq;
fn decode_with_annotation<
T: Reader,
S: DescriptionSink
>(&self, inst: &mut A::Instruction, words: &mut T, sink: &mut S) -> Result<(), A::DecodeError>;
}
```
where `FieldDescription` lets callers that operate generically over spans do *something* with them. implementations can use `id` to tag descriptions that should be ordered together, regardless of the actual order the decoder reports them in. for some architectures, fields parsed later in decoding may influence the understanding of earlier fields, so reporting spans in `id`-order up front is an unreasonable burden. consider an x86 instruction, `660f6ec0` - the leading `66` is an operand-size override, but only after reading `0f6e` is it known that that prefix is changing the operands from `mm`/`dword` registers to `xmm`/`qword` registers. in fact this is only known _after_ reporting the opcode of `0f6e`, too.
`start` and `end` are bit offsets where a `description` applies. `description`s can overlap in part, or in full. exact bit order is known only by the architecture being decoded; is the order `0-7,8-15,16-23,24-31`, `7-0,15-8,23-16,31-24`, or something else? i'm not sure trying to encode that in `yaxpeax-arch` traits is useful right now. `start` and `end` are `u32` because in my professional opinion, `u16` is cursed, `u8` isn't large enough, and `u32` is the next smallest size. `id()` returns a `u32` because i never want to think of `id` space constraints; even if `id` encoded a `major.minor`-style pair of ordering components, the most constrained layout would be `u16.u16` for at most 65536 values in major or minor. that's a big instruction.
### implementation
i've added WIP support for span reporting to `msp430`, `ia64`, and `x86` decoders. i extended `yaxpeax-dis` to [make pretty lines](https://twitter.com/iximeow/status/1423930207614889984). more could be said about that; `id`-order is expected to be, roughtly, the order an instruction is decoded. some instructions sets keep the "first" bits as the low-order bits, some others use the higher bits first. so respecting `id`-order necessarily means some instruction sets will have fields "backwards" and make lines extra confusing.
decoders probably ought to indicate boundaries for significant parts of decoding, lest large instructions [like itanium](https://twitter.com/iximeow/status/1424092536071618561) be a nebulous mess. maybe `FieldDescription` could have an `is_separator()` to know when an element (and its bit range) indicates the end of part of an instruction?
for the most part, things work great. `yaxpeax-x86` had a minor performance regression. tracking it down wasn't too bad: the first one was because `sink` is a fifth argument for a non-inlined function. at this point most ABIs start spilling to memory. so an unused `sink` caused an extra stack write. this was a measurable overhead. the second regression was again pretty simple looking at `disas-bench` builds:
```sh
diff \
` # a typical non-formatting build, from cratesio yaxpeax-x86 1.0.4 ` \
<(objdump -d bench-yaxpeax-no-fmt | grep -o ' .*long_mode.*>:')
` # a non-formatting build, from the local patch of yaxpeax-x86 with annotation reported to a no-op sink ` \
<(objdump -d bench-yaxpeax-no-fmt-no-annotation | grep -o ' .*long_mode.*>:')
```
the entire diff output:
```diff
> <_ZN11yaxpeax_x869long_mode8read_sib17hdc339ef7a182098aE>:
```
indeed, [`read_sib`](https://github.com/iximeow/yaxpeax-x86/blob/4371ed02ac30cb56ec4ddbf60c87e85c183d860b/src/long_mode/mod.rs#L5769-L5770) is not written as `inline(always)`, so it's possible this might not get inlined sometimes. since the only difference to `read_sib` is an extra parameter, for which all calls are no-ops that ignore arguments, i'm surprised to see the change, anyway. adding `#[inline(always)]` to `read_sib` returned `yaxpeax-x86` to "same-as-before" decode throughput.
in the process, i found a slight optimization for `read_sib` that removed a few extra branches from the function. the scrutiny was good after all.
### conclusion
in summary, it works. it doesn't slow down callers that don't need spans of information. decoders can implement it optionally and at their leisure, without being ineligible for analysis-oriented libraries.
this is almost certainly going to be in `yaxpeax-arch 0.2.6` with implementations trickling into decoders whenever it seems like fun.
yaxpeax-arch-0.3.2/goodfile 0000644 0000000 0000000 00000006177 10461020230 0013746 0 ustar 0000000 0000000 Build.dependencies({"git", "make", "rustc", "cargo", "rustup"})
Step.start("crate")
Step.push("build")
Build.run({"cargo", "build"})
-- and now that some code is conditional on target arch, at least try to build
-- for other architectures even if we might not be able to run on them.
Build.run({"rustup", "target", "add", "wasm32-wasi"})
Build.run({"cargo", "build", "--no-default-features", "--target", "wasm32-wasi"})
Step.advance("test")
-- TODO: set `-D warnings` here and below...
Build.run({"cargo", "test"}, {name="test default features"})
-- `cargo test` ends up running doc tests. great! but yaxpeax-arch's docs reference items in std only.
-- so for other feature combinations, skip doc tests. do this by passing `--tests` explicitly,
-- which disables the automagic "run everything" settings.
Build.run({"cargo", "test", "--no-default-features", "--tests"}, {name="test no features"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std"}, {name="test std only"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "colors"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "use-serde"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "address-parse"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "alloc"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "color-new"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,colors"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,use-serde"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,address-parse"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,address-parse,alloc"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "use-serde,colors,address-parse"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "use-serde,colors,address-parse,alloc"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,colors,address-parse"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,colors,address-parse,alloc"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,use-serde,colors"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "std,use-serde,colors,alloc"}, {name="test feature combinations"})
Build.run({"cargo", "test", "--no-default-features", "--tests", "--features", "color-new,alloc"}, {name="test feature combinations"})
yaxpeax-arch-0.3.2/rust-toolchain 0000644 0000000 0000000 00000000007 10461020230 0015113 0 ustar 0000000 0000000 1.71.0
yaxpeax-arch-0.3.2/src/address/mod.rs 0000644 0000000 0000000 00000030117 10461020230 0015563 0 ustar 0000000 0000000 use core::hash::Hash;
use core::fmt;
use core::ops::{Add, Sub, AddAssign, SubAssign};
use num_traits::identities;
use num_traits::{Bounded, WrappingAdd, WrappingSub, CheckedAdd, Zero, One};
#[cfg(feature="use-serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature="use-serde")]
pub trait AddressDiffAmount: Copy + Clone + PartialEq + PartialOrd + Eq + Ord + identities::Zero + identities::One + Serialize + for<'de> Deserialize<'de> {}
#[cfg(not(feature="use-serde"))]
pub trait AddressDiffAmount: Copy + Clone + PartialEq + PartialOrd + Eq + Ord + identities::Zero + identities::One {}
impl AddressDiffAmount for u64 {}
impl AddressDiffAmount for u32 {}
impl AddressDiffAmount for u16 {}
impl AddressDiffAmount for usize {}
/// a struct describing the differece between some pair of `A: Address`. this is primarily useful
/// in describing the size of an instruction, or the relative offset of a branch.
///
/// for any address type `A`, the following must hold:
/// ```rust
/// use yaxpeax_arch::AddressBase;
/// fn diff_check(left: A, right: A) {
/// let diff = left.diff(&right);
/// if let Some(offset) = diff {
/// assert_eq!(left.wrapping_offset(offset), right);
/// }
/// }
/// ```
///
/// which is to say, `yaxpeax` assumes associativity holds when `diff` yields a `Some`.
#[cfg(feature="use-serde")]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct AddressDiff {
// the AddressDiffAmount trait fools `Deserialize`'s proc macro, so we have to explicitly write
// the bound serde should use.
#[serde(bound(deserialize = "T::Diff: AddressDiffAmount"))]
amount: T::Diff,
}
/// a struct describing the differece between some pair of `A: Address`. this is primarily useful
/// in describing the size of an instruction, or the relative offset of a branch.
///
/// for any address type `A`, the following must hold:
/// ```rust
/// use yaxpeax_arch::AddressBase;
/// fn diff_check(left: A, right: A) {
/// let diff = left.diff(&right);
/// if let Some(offset) = diff {
/// assert_eq!(left.wrapping_offset(offset), right);
/// }
/// }
/// ```
///
/// which is to say, `yaxpeax` assumes associativity holds when `diff` yields a `Some`.
#[cfg(not(feature="use-serde"))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct AddressDiff {
amount: T::Diff,
}
impl AddressDiff {
pub fn from_const(amount: T::Diff) -> Self {
AddressDiff { amount }
}
pub fn to_const(&self) -> T::Diff {
self.amount
}
}
impl fmt::Debug for AddressDiff where T::Diff: fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AddressDiff({:?})", self.amount)
}
}
impl AddressDiff {
pub fn one() -> Self {
AddressDiff {
amount: ::Diff::one(),
}
}
pub fn zero() -> Self {
AddressDiff {
amount: ::Diff::zero(),
}
}
}
impl Sub> for u16 {
type Output = Self;
fn sub(self, other: AddressDiff) -> Self::Output {
self - other.amount
}
}
impl Sub> for u32 {
type Output = Self;
fn sub(self, other: AddressDiff) -> Self::Output {
self - other.amount
}
}
impl Sub> for u64 {
type Output = Self;
fn sub(self, other: AddressDiff) -> Self::Output {
self - other.amount
}
}
impl Sub> for usize {
type Output = Self;
fn sub(self, other: AddressDiff) -> Self::Output {
self - other.amount
}
}
impl Add> for u16 {
type Output = Self;
fn add(self, other: AddressDiff) -> Self::Output {
self + other.amount
}
}
impl Add> for u32 {
type Output = Self;
fn add(self, other: AddressDiff) -> Self::Output {
self + other.amount
}
}
impl Add> for u64 {
type Output = Self;
fn add(self, other: AddressDiff) -> Self::Output {
self + other.amount
}
}
impl Add> for usize {
type Output = Self;
fn add(self, other: AddressDiff) -> Self::Output {
self + other.amount
}
}
impl SubAssign> for u16 {
fn sub_assign(&mut self, other: AddressDiff) {
*self -= other.amount;
}
}
impl SubAssign> for u32 {
fn sub_assign(&mut self, other: AddressDiff) {
*self -= other.amount;
}
}
impl SubAssign> for u64 {
fn sub_assign(&mut self, other: AddressDiff) {
*self -= other.amount;
}
}
impl SubAssign> for usize {
fn sub_assign(&mut self, other: AddressDiff) {
*self -= other.amount;
}
}
impl AddAssign> for u16 {
fn add_assign(&mut self, other: AddressDiff) {
*self += other.amount;
}
}
impl AddAssign> for u32 {
fn add_assign(&mut self, other: AddressDiff) {
*self += other.amount;
}
}
impl AddAssign> for u64 {
fn add_assign(&mut self, other: AddressDiff) {
*self += other.amount;
}
}
impl AddAssign> for usize {
fn add_assign(&mut self, other: AddressDiff) {
*self += other.amount;
}
}
pub trait AddressBase where Self:
AddressDisplay +
Copy + Clone + Sized + Hash +
Ord + Eq + PartialEq + Bounded +
Add, Output=Self> + Sub, Output=Self> +
AddAssign> + SubAssign> +
identities::Zero +
Hash {
type Diff: AddressDiffAmount;
fn to_linear(&self) -> usize;
/// compute the `AddressDiff` beetween `self` and `other`.
///
/// may return `None` if the two addresses aren't comparable. for example, if a pair of
/// addresses are a data-space address and code-space address, there may be no scalar that can
/// describe the difference between them.
fn diff(&self, other: &Self) -> Option>;
/*
{
Some(AddressDiff { amount: self.wrapping_sub(other) })
}
*/
fn wrapping_offset(&self, other: AddressDiff) -> Self;
/*
{
self.wrapping_add(&other.amount)
}
*/
fn checked_offset(&self, other: AddressDiff) -> Option;
/*
{
self.checked_add(&other.amount)
}
*/
}
#[cfg(all(feature="use-serde", feature="address-parse"))]
pub trait Address where Self:
AddressBase +
Serialize + for<'de> Deserialize<'de> +
AddrParse {
}
#[cfg(all(feature="use-serde", not(feature="address-parse")))]
pub trait Address where Self:
AddressBase +
Serialize + for<'de> Deserialize<'de> {
}
#[cfg(all(not(feature="use-serde"), feature="address-parse"))]
pub trait Address where Self:
AddressBase + AddrParse {
}
#[cfg(all(not(feature="use-serde"), not(feature="address-parse")))]
pub trait Address where Self: AddressBase { }
impl AddressBase for u16 {
type Diff = Self;
fn to_linear(&self) -> usize { *self as usize }
fn diff(&self, other: &Self) -> Option> {
Some(AddressDiff { amount: self.wrapping_sub(other) })
}
fn wrapping_offset(&self, other: AddressDiff) -> Self {
self.wrapping_add(&other.amount)
}
fn checked_offset(&self, other: AddressDiff) -> Option {
self.checked_add(&other.amount)
}
}
impl Address for u16 {}
impl AddressBase for u32 {
type Diff = Self;
fn to_linear(&self) -> usize { *self as usize }
fn diff(&self, other: &Self) -> Option> {
Some(AddressDiff { amount: self.wrapping_sub(other) })
}
fn wrapping_offset(&self, other: AddressDiff) -> Self {
self.wrapping_add(&other.amount)
}
fn checked_offset(&self, other: AddressDiff) -> Option {
self.checked_add(&other.amount)
}
}
impl Address for u32 {}
impl AddressBase for u64 {
type Diff = Self;
fn to_linear(&self) -> usize { *self as usize }
fn diff(&self, other: &Self) -> Option> {
Some(AddressDiff { amount: self.wrapping_sub(other) })
}
fn wrapping_offset(&self, other: AddressDiff) -> Self {
self.wrapping_add(&other.amount)
}
fn checked_offset(&self, other: AddressDiff) -> Option {
self.checked_add(&other.amount)
}
}
impl Address for u64 {}
impl AddressBase for usize {
type Diff = Self;
fn to_linear(&self) -> usize { *self }
fn diff(&self, other: &Self) -> Option> {
Some(AddressDiff { amount: self.wrapping_sub(other) })
}
fn wrapping_offset(&self, other: AddressDiff) -> Self {
self.wrapping_add(&other.amount)
}
fn checked_offset(&self, other: AddressDiff) -> Option {
self.checked_add(&other.amount)
}
}
impl Address for usize {}
pub trait AddressDisplay {
type Show: fmt::Display;
fn show(&self) -> Self::Show;
}
impl AddressDisplay for usize {
type Show = AddressDisplayUsize;
fn show(&self) -> AddressDisplayUsize {
AddressDisplayUsize(*self)
}
}
#[repr(transparent)]
pub struct AddressDisplayUsize(usize);
impl fmt::Display for AddressDisplayUsize {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl AddressDisplay for u64 {
type Show = AddressDisplayU64;
fn show(&self) -> AddressDisplayU64 {
AddressDisplayU64(*self)
}
}
#[repr(transparent)]
pub struct AddressDisplayU64(u64);
impl fmt::Display for AddressDisplayU64 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl AddressDisplay for u32 {
type Show = AddressDisplayU32;
fn show(&self) -> AddressDisplayU32 {
AddressDisplayU32(*self)
}
}
#[repr(transparent)]
pub struct AddressDisplayU32(u32);
impl fmt::Display for AddressDisplayU32 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl AddressDisplay for u16 {
type Show = AddressDisplayU16;
fn show(&self) -> AddressDisplayU16 {
AddressDisplayU16(*self)
}
}
#[repr(transparent)]
pub struct AddressDisplayU16(u16);
impl fmt::Display for AddressDisplayU16 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
/*
* TODO: this should be FromStr.
* that would require newtyping address primitives, though
*
* this is not out of the question, BUT is way more work than
* i want to put in right now
*
* this is one of those "clean it up later" situations
*/
#[cfg(feature="address-parse")]
use core::str::FromStr;
#[cfg(feature="address-parse")]
pub trait AddrParse: Sized {
type Err;
fn parse_from(s: &str) -> Result;
}
#[cfg(feature="address-parse")]
impl AddrParse for usize {
type Err = core::num::ParseIntError;
fn parse_from(s: &str) -> Result {
if s.starts_with("0x") {
usize::from_str_radix(&s[2..], 16)
} else {
usize::from_str(s)
}
}
}
#[cfg(feature="address-parse")]
impl AddrParse for u64 {
type Err = core::num::ParseIntError;
fn parse_from(s: &str) -> Result {
if s.starts_with("0x") {
u64::from_str_radix(&s[2..], 16)
} else {
u64::from_str(s)
}
}
}
#[cfg(feature="address-parse")]
impl AddrParse for u32 {
type Err = core::num::ParseIntError;
fn parse_from(s: &str) -> Result {
if s.starts_with("0x") {
u32::from_str_radix(&s[2..], 16)
} else {
u32::from_str(s)
}
}
}
#[cfg(feature="address-parse")]
impl AddrParse for u16 {
type Err = core::num::ParseIntError;
fn parse_from(s: &str) -> Result {
if s.starts_with("0x") {
u16::from_str_radix(&s[2..], 16)
} else {
u16::from_str(s)
}
}
}
yaxpeax-arch-0.3.2/src/annotation/mod.rs 0000644 0000000 0000000 00000015114 10461020230 0016310 0 ustar 0000000 0000000 //! traits (and convenient impls) for decoders that also produce descriptions of parsed bit fields.
//!
//! the design of this API is discussed in [`yaxpeax-arch`
//! documentation](https://github.com/iximeow/yaxpeax-arch/blob/no-gods-no-/docs/0001-AnnotatingDecoder.md#descriptionsink).
//!
//! ## usage
//!
//! [`AnnotatingDecoder::decode_with_annotation`] decodes an instruction much like
//! [`crate::Decoder::decode_into`], but also reports descriptions of bit fields to a provided
//! [`DescriptionSink`]. [`VecSink`] is likely the `DescriptionSink` of interest to retain fields;
//! decoders are not required to make any guarantees about the order of descriptions, either by the
//! description's associated [`FieldDescription::id`], or with respect to the bits a
//! `FieldDescription` is reported against. fields may be described by multiple `FieldDescription`
//! with matching `id` and `desc` -- this is to describe data in an instruction where
//! non-contiguous bits are taken together for a single detail. for these cases, the various
//! `FieldDescription` must compare equal, and users of `yaxpeax-arch` can rely on this equivalence
//! for grouping bit ranges.
//!
//! in a generic setting, there isn't much to do with a `FieldDescription` other than display it. a
//! typical use might look something like:
//! ```
//! #[cfg(feature="std")]
//! # {
//! use core::fmt;
//!
//! use yaxpeax_arch::annotation::{AnnotatingDecoder, VecSink};
//! use yaxpeax_arch::{Arch, Reader, U8Reader};
//!
//! fn show_field_descriptions(decoder: A::Decoder, buf: &[u8])
//! where
//! A::Decoder: AnnotatingDecoder,
//! A::Instruction: fmt::Display, for<'data> U8Reader<'data>: Reader,
//! {
//! let mut inst = A::Instruction::default();
//! let mut reader = U8Reader::new(buf);
//! let mut sink: VecSink<>::FieldDescription> = VecSink::new();
//!
//! decoder.decode_with_annotation(&mut inst, &mut reader, &mut sink).unwrap();
//!
//! println!("decoded instruction {}", inst);
//! for (start, end, desc) in sink.records.iter() {
//! println!(" bits [{}, {}]: {}", start, end, desc);
//! }
//! }
//! # }
//! ```
//!
//! note that the range `[start, end]` for a reported span is _inclusive_. the `end`-th bit of a
//! an instruction's bit stream is described by the description.
//!
//! ## implementation guidance
//!
//! the typical implementation pattern is that an architecture's `Decoder` implements [`crate::Decoder`]
//! _and_ [`AnnotatingDecoder`], then callers are free to choose which style of decoding they want.
//! [`NullSink`] has a blanket impl of [`DescriptionSink`] for all possible descriptions, and
//! discards reported field descriptions. `decode_with_annotation` with annotations reported to a
//! `NullSink` must be functionally identical to a call to `Decoder::decode_into`.
//!
//! the important points:
//!
//! * `AnnotatingDecoder` is an **optional** implementation for decoders.
//! * `FieldDescription` in general is oriented towards human-directed output, but implementations
//! can be as precise as they want.
//! * since bit/byte order varies from architecture to architecture, a field's `start` and `end`
//! are defined with some ordering from the corresponding decoder crate. crates should describe the
//! bit ordering they select, and where possible, the bit ordering they describe should match
//! relevant ISA mauals.
//! * `FieldDescription` that return true for [`FieldDescription::is_separator`] are an exception
//! to bit span inclusivity: for these descriptions, the bit range should be `[b, b]` where `b` is
//! the last bit before the boundary being delimited. unlike other descriptions, `is_separator`
//! descriptions describe the space between bits `b` and `b+1`.
//! * if a description is to cover multiple bit fields, the reported `FieldDescription` must
//! be identical on `id` and `desc` for all involved bit fields.
use crate::{Arch, Reader};
use core::fmt::Display;
/// implementers of `DescriptionSink` receive descriptions of an instruction's disassembly process
/// and relevant offsets in the bitstream being decoded. descriptions are archtecture-specific, and
/// architectures are expected to be able to turn the bit-level `start` and `width` values into a
/// meaningful description of bits in the original instruction stream.
pub trait DescriptionSink {
/// inform this `DescriptionSink` of a `description` that was informed by bits `start` to
/// `end` from the start of an instruction's decoding. `start` and `end` are only relative the
/// instruction being decoded when this sink `DescriptionSink` provided, so they will have no
/// relation to the position in an underlying data stream used for past or future instructions.
fn record(&mut self, start: u32, end: u32, description: Descriptor);
}
pub struct NullSink;
impl DescriptionSink for NullSink {
fn record(&mut self, _start: u32, _end: u32, _description: T) { }
}
#[cfg(feature = "alloc")]
mod vec_sink {
use alloc::vec::Vec;
use core::fmt::Display;
use crate::annotation::DescriptionSink;
pub struct VecSink {
pub records: Vec<(u32, u32, T)>
}
impl VecSink {
pub fn new() -> Self {
VecSink { records: Vec::new() }
}
pub fn into_inner(self) -> Vec<(u32, u32, T)> {
self.records
}
}
impl DescriptionSink for VecSink {
fn record(&mut self, start: u32, end: u32, description: T) {
self.records.push((start, end, description));
}
}
}
#[cfg(feature = "alloc")]
pub use vec_sink::VecSink;
pub trait FieldDescription {
fn id(&self) -> u32;
fn is_separator(&self) -> bool;
}
/// an interface to decode [`Arch::Instruction`] words from a reader of [`Arch::Word`]s, with the
/// decoder able to report descriptions of bits or fields in the instruction to a sink implementing
/// [`DescriptionSink`]. the sink may be [`NullSink`] to discard provided data. decoding with a
/// `NullSink` should behave identically to `Decoder::decode_into`. implementers are recommended to
/// implement `Decoder::decode_into` as a call to `AnnotatingDecoder::decode_with_annotation` if
/// implementing both traits.
pub trait AnnotatingDecoder {
type FieldDescription: FieldDescription + Clone + Display + PartialEq;
fn decode_with_annotation<
T: Reader,
S: DescriptionSink
>(&self, inst: &mut A::Instruction, words: &mut T, sink: &mut S) -> Result<(), A::DecodeError>;
}
yaxpeax-arch-0.3.2/src/color.rs 0000644 0000000 0000000 00000031131 10461020230 0014472 0 ustar 0000000 0000000 use core::fmt::{self, Display, Formatter};
#[cfg(feature="colors")]
use crossterm::style;
#[cfg(feature="colors")]
pub enum Colored {
Color(T, style::Color),
Just(T)
}
#[cfg(feature="colors")]
impl Display for Colored {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match self {
Colored::Color(t, before) => {
use crossterm::style::Stylize;
write!(fmt, "{}", style::style(t).with(*before))
},
Colored::Just(t) => {
write!(fmt, "{}", t)
}
}
}
}
#[cfg(not(feature="colors"))]
pub enum Colored {
Just(T)
}
#[cfg(not(feature="colors"))]
impl Display for Colored {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match self {
Colored::Just(t) => {
write!(fmt, "{}", t)
}
}
}
}
pub trait YaxColors {
fn arithmetic_op(&self, t: T) -> Colored;
fn stack_op(&self, t: T) -> Colored;
fn nop_op(&self, t: T) -> Colored;
fn stop_op(&self, t: T) -> Colored;
fn control_flow_op(&self, t: T) -> Colored;
fn data_op(&self, t: T) -> Colored;
fn comparison_op(&self, t: T) -> Colored;
fn invalid_op(&self, t: T) -> Colored;
fn platform_op(&self, t: T) -> Colored;
fn misc_op(&self, t: T) -> Colored;
fn register(&self, t: T) -> Colored;
fn program_counter(&self, t: T) -> Colored;
fn number(&self, t: T) -> Colored;
fn zero(&self, t: T) -> Colored;
fn one(&self, t: T) -> Colored;
fn minus_one(&self, t: T) -> Colored;
fn address(&self, t: T) -> Colored;
fn symbol(&self, t: T) -> Colored;
fn function(&self, t: T) -> Colored;
}
pub struct NoColors;
impl YaxColors for NoColors {
fn arithmetic_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn stack_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn nop_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn stop_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn control_flow_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn data_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn comparison_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn invalid_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn platform_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn misc_op(&self, t: T) -> Colored {
Colored::Just(t)
}
fn register(&self, t: T) -> Colored {
Colored::Just(t)
}
fn program_counter(&self, t: T) -> Colored {
Colored::Just(t)
}
fn number(&self, t: T) -> Colored {
Colored::Just(t)
}
fn zero(&self, t: T) -> Colored {
Colored::Just(t)
}
fn one(&self, t: T) -> Colored {
Colored::Just(t)
}
fn minus_one(&self, t: T) -> Colored {
Colored::Just(t)
}
fn address(&self, t: T) -> Colored {
Colored::Just(t)
}
fn symbol(&self, t: T) -> Colored {
Colored::Just(t)
}
fn function(&self, t: T) -> Colored {
Colored::Just(t)
}
}
pub trait Colorize {
fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result;
}
#[cfg(feature="colors")]
pub use termion_color::ColorSettings;
#[cfg(feature="colors")]
mod termion_color {
use core::fmt::Display;
use crossterm::style;
use crate::color::{Colored, YaxColors};
#[cfg(feature="use-serde")]
impl serde::Serialize for ColorSettings {
fn serialize(&self, serializer: S) -> Result {
use serde::ser::SerializeStruct;
let s = serializer.serialize_struct("ColorSettings", 0)?;
s.end()
}
}
pub struct ColorSettings {
arithmetic: style::Color,
stack: style::Color,
nop: style::Color,
stop: style::Color,
control: style::Color,
data: style::Color,
comparison: style::Color,
invalid: style::Color,
platform: style::Color,
misc: style::Color,
register: style::Color,
program_counter: style::Color,
number: style::Color,
zero: style::Color,
one: style::Color,
minus_one: style::Color,
function: style::Color,
symbol: style::Color,
address: style::Color,
}
impl Default for ColorSettings {
fn default() -> ColorSettings {
ColorSettings {
arithmetic: style::Color::Yellow,
stack: style::Color::DarkMagenta,
nop: style::Color::DarkBlue,
stop: style::Color::Red,
control: style::Color::DarkGreen,
data: style::Color::Magenta,
comparison: style::Color::DarkYellow,
invalid: style::Color::DarkRed,
platform: style::Color::DarkCyan,
misc: style::Color::Cyan,
register: style::Color::DarkCyan,
program_counter: style::Color::DarkRed,
number: style::Color::White,
zero: style::Color::White,
one: style::Color::White,
minus_one: style::Color::White,
function: style::Color::Green,
symbol: style::Color::Green,
address: style::Color::DarkGreen,
}
}
}
impl YaxColors for ColorSettings {
fn arithmetic_op(&self, t: T) -> Colored {
Colored::Color(t, self.arithmetic)
}
fn stack_op(&self, t: T) -> Colored {
Colored::Color(t, self.stack)
}
fn nop_op(&self, t: T) -> Colored {
Colored::Color(t, self.nop)
}
fn stop_op(&self, t: T) -> Colored {
Colored::Color(t, self.stop)
}
fn control_flow_op(&self, t: T) -> Colored {
Colored::Color(t, self.control)
}
fn data_op(&self, t: T) -> Colored {
Colored::Color(t, self.data)
}
fn comparison_op(&self, t: T) -> Colored {
Colored::Color(t, self.comparison)
}
fn invalid_op(&self, t: T) -> Colored {
Colored::Color(t, self.invalid)
}
fn misc_op(&self, t: T) -> Colored {
Colored::Color(t, self.misc)
}
fn platform_op(&self, t: T) -> Colored {
Colored::Color(t, self.platform)
}
fn register(&self, t: T) -> Colored {
Colored::Color(t, self.register)
}
fn program_counter(&self, t: T) -> Colored {
Colored::Color(t, self.program_counter)
}
fn number(&self, t: T) -> Colored {
Colored::Color(t, self.number)
}
fn zero(&self, t: T) -> Colored {
Colored::Color(t, self.zero)
}
fn one(&self, t: T) -> Colored {
Colored::Color(t, self.one)
}
fn minus_one(&self, t: T) -> Colored {
Colored::Color(t, self.minus_one)
}
fn address(&self, t: T) -> Colored {
Colored::Color(t, self.address)
}
fn symbol(&self, t: T) -> Colored {
Colored::Color(t, self.symbol)
}
fn function(&self, t: T) -> Colored {
Colored::Color(t, self.function)
}
}
impl <'a> YaxColors for Option<&'a ColorSettings> {
fn arithmetic_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.arithmetic_op(t) }
None => { Colored::Just(t) }
}
}
fn stack_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.stack_op(t) }
None => { Colored::Just(t) }
}
}
fn nop_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.nop_op(t) }
None => { Colored::Just(t) }
}
}
fn stop_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.stop_op(t) }
None => { Colored::Just(t) }
}
}
fn control_flow_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.control_flow_op(t) }
None => { Colored::Just(t) }
}
}
fn data_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.data_op(t) }
None => { Colored::Just(t) }
}
}
fn comparison_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.comparison_op(t) }
None => { Colored::Just(t) }
}
}
fn invalid_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.invalid_op(t) }
None => { Colored::Just(t) }
}
}
fn misc_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.misc_op(t) }
None => { Colored::Just(t) }
}
}
fn platform_op(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.platform_op(t) }
None => { Colored::Just(t) }
}
}
fn register(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.register(t) }
None => { Colored::Just(t) }
}
}
fn program_counter(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.program_counter(t) }
None => { Colored::Just(t) }
}
}
fn number(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.number(t) }
None => { Colored::Just(t) }
}
}
fn zero(&self, t: T) -> Colored {
match self {
Some(colors) => { colors.zero(t) }
None => { Colored::Just(t) }
}
}
fn one