Rename kamil_adc to rph_radar and fix its data path
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# RadioPhotonic radar (RPH) collector
|
||||
|
||||
Application-owned acquisition binary for the **L-Card E-502** ADC. It captures the
|
||||
dual-channel main + reference IQ stream and publishes complete `RPH1` sweeps over
|
||||
a PTY. `RphRadarService` in `python_app/hardware_full/rph_radar` reads and
|
||||
processes that stream for `python_app.scripts.rph_radar_raw_producer`.
|
||||
|
||||
This is the data *source* for `radar.model == "rph_radar"`, paired with the Python
|
||||
`rph_radar_raw_producer`. LibreVNA acquisition uses Python drivers.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Contents |
|
||||
|------|----------|
|
||||
| `src/` | Collector implementation (`main.cpp`) and the PTY writer. |
|
||||
| `include/` | PTY header and `switch_sequencer.h`. |
|
||||
| `vendor/lcard/` | L-Card X502/E502 SDK headers (`x502api.h`, `e502api.h`, `lcard_pstdint.h`), vendored verbatim so the collector builds with no external include path. |
|
||||
|
||||
In switch-aware mode, `SwitchSequencer` drives the H7992 switches at actual
|
||||
DI_SYN2 sweep ends. Duration limits and Stop never advance the switches. Every
|
||||
physical combination change discards the remaining decoded ADC/DIN block and
|
||||
recreates the source session after `max(1 ms, run.settling_ms)` has elapsed.
|
||||
Timestamps never decide whether a sweep belongs to the new combination. The
|
||||
collector completes a cycle only after a full, clean sweep for each combination.
|
||||
An unchanged combination keeps the existing session.
|
||||
|
||||
A restart stops cyclic DOUT with `WAIT_DONE`, stops and disables the streams,
|
||||
restores DO2 high / DO1 and DO8 low, then closes and frees the SDK descriptor.
|
||||
After settling, a new descriptor is created, opened and configured. The collector
|
||||
reloads the DO1/DO8 pattern, waits for `OutCycleSetup(WAIT_DONE)`, and starts
|
||||
ADC/DIN/DOUT together. Pending samples and phase accumulators are cleared;
|
||||
source sweep and cycle IDs remain continuous. The initial high DI_SYN2 portion
|
||||
is dirty: acquisition waits for a complete rising-to-falling sweep. This can
|
||||
skip a variable number of physical sweeps while reopening; no fixed skip count
|
||||
or firmware-pause estimate is used. Hardware validation must measure reopen
|
||||
latency, USB resource release and multi-combination throughput.
|
||||
|
||||
The application uses the internal ADC clock. ADC and DIN start together,
|
||||
continuously; DI_SYN2 gates sweeps in software. Source times are the monotonic
|
||||
clock immediately before `StreamsStart` plus the ADC sample index divided by the
|
||||
configured sample rate. These are source-clock estimates, not hardware timestamps;
|
||||
startup latency shifts the initial epoch earlier. USB and PTY queueing do not
|
||||
shift timestamps to the time Python reads a sweep. An estimated source timestamp
|
||||
ahead of receipt stops acquisition instead of publishing a future capture window.
|
||||
ADC/host drift can trigger this guard during long single-combination runs; its
|
||||
bound and the accuracy of capture windows need measurement on hardware. Drift
|
||||
cannot make data from an old SDK session clean after a physical switch.
|
||||
|
||||
The collector uses the common H7992 driver, with its own include set and
|
||||
`build/rph/` object tree.
|
||||
|
||||
## PTY format
|
||||
|
||||
Each packet is a 48-byte little-endian header (`<4sIQQQQHHI`) followed by the
|
||||
payload: `RPH1`, payload bytes (u32), source sweep ID, cycle ID, start/end ns
|
||||
(four u64), input/output positions (two u16), flags (u32; bit 0 means dirty).
|
||||
Without collector switching both positions are `0xffff`, and cycle equals sweep
|
||||
ID. Payload points remain four u16 words: marker (`0x000a` main, `0x00a8`
|
||||
reference), step index, signed I, signed Q. There are no boundary or combo frames.
|
||||
The queue drops complete sweeps on overflow; a packet already being written is
|
||||
never truncated. Maximum payload is 1,048,544 bytes. Python matches complete
|
||||
cycles. Python-controlled switching closes the previous collector and PTY reader,
|
||||
then starts a new collector after settling; phase-processing state is preserved.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
make rph_radar_collector # -> build/bin/rph_radar_collector
|
||||
```
|
||||
|
||||
It is **not** part of `make all`; `start.sh` builds it automatically when the
|
||||
active config selects `radar.model == "rph_radar"`, unless `--skip-build` is set.
|
||||
The Linux build uses the repository's common sources and vendored headers and
|
||||
links with `-pthread -ldl -lutil`; no installed L-Card SDK is needed to build it.
|
||||
|
||||
## Runtime
|
||||
|
||||
The collector loads the proprietary `libx502api.so` / `libe502api.so` at run time
|
||||
via `dlopen`, so those libraries must be discoverable by the dynamic loader.
|
||||
The RPH radar service prepends `~/.local/lib` (their default install location)
|
||||
unless the config pins `LD_LIBRARY_PATH` in `radar.rph_radar.env`.
|
||||
|
||||
There is one acquisition path: USB, differential ADC channels 2/3, maximum
|
||||
internal-clock rate, rising/falling DI_SYN2 gating, DO1 pair subtraction and DO8
|
||||
reference overlay. `radar.rph_radar.args` sets ADC range, internal reference,
|
||||
maximum sweep duration and DO8 period. The example's fixed-path options remain
|
||||
accepted as guards; incompatible modes and unused diagnostic options are rejected.
|
||||
The service appends `tty:<path>` from `radar.rph_radar.tty_path`.
|
||||
|
||||
When both RF switches use `driver_mode: "native"`, Python passes
|
||||
`switching:<JSON>` with the already decoded input/output switch settings, combos
|
||||
and settling time. The collector reads no profile file. Otherwise, Python selects
|
||||
the switches and starts a new collector after settling when the combination
|
||||
changes. Calibration captures also use this Python-controlled path.
|
||||
|
||||
The launching service owns the collector process and forwards its output to the
|
||||
application log. On Linux, parent death sends SIGKILL to the collector; normal
|
||||
Stop drains and closes the service's own child. No global process-name kill is
|
||||
used.
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "shared_types.hpp"
|
||||
#include "h7992_minimal_driver.hpp"
|
||||
|
||||
namespace radar::rph {
|
||||
|
||||
// Every real GPIO transition invalidates the ADC session. No timestamp can
|
||||
// declare queued data clean; only a newly started source session can do that.
|
||||
class SwitchSequencer {
|
||||
public:
|
||||
struct Settings {
|
||||
/// Combinations to cycle through, one sweep each, in order. Empty disables
|
||||
/// the sequencer entirely (the collector then streams a single passthrough
|
||||
/// channel exactly as before).
|
||||
std::vector<radar::ipc::ComboKey> combos{};
|
||||
/// RF settling allowance after both GPIO writes complete.
|
||||
double settle_ms = 1.0;
|
||||
};
|
||||
|
||||
SwitchSequencer(
|
||||
Settings settings,
|
||||
std::unique_ptr<drivers::H7992MinimalDriver> input_switch,
|
||||
std::unique_ptr<drivers::H7992MinimalDriver> output_switch
|
||||
)
|
||||
: settings_(std::move(settings)),
|
||||
input_switch_(std::move(input_switch)),
|
||||
output_switch_(std::move(output_switch)) {}
|
||||
|
||||
[[nodiscard]] auto enabled() const -> bool { return !settings_.combos.empty(); }
|
||||
|
||||
/// Open both switch drivers and park at the first combination.
|
||||
void open() {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
output_switch_->open();
|
||||
input_switch_->open();
|
||||
index_ = 0;
|
||||
applied_ = settings_.combos.front();
|
||||
apply(applied_);
|
||||
upcoming_dirty_ = true;
|
||||
}
|
||||
|
||||
/// Combination the in-progress / upcoming sweep is captured under.
|
||||
[[nodiscard]] auto current_combo() const -> radar::ipc::ComboKey {
|
||||
return settings_.combos[index_];
|
||||
}
|
||||
|
||||
/// Whether the in-progress / upcoming sweep is expected to straddle a switch
|
||||
/// transition and must be dropped by the consumer.
|
||||
[[nodiscard]] auto current_dirty() const -> bool { return upcoming_dirty_; }
|
||||
|
||||
[[nodiscard]] auto cycle_id() const -> std::uint64_t { return cycle_id_; }
|
||||
|
||||
[[nodiscard]] auto settled_after_ns() const -> std::uint64_t { return settled_after_ns_; }
|
||||
void session_started() { upcoming_dirty_ = false; }
|
||||
|
||||
bool on_sweep_end(bool complete = true) {
|
||||
if (!enabled() || !complete || upcoming_dirty_) { return false; }
|
||||
index_ = (index_ + 1U) % settings_.combos.size();
|
||||
if (index_ == 0) { ++cycle_id_; }
|
||||
const radar::ipc::ComboKey next = settings_.combos[index_];
|
||||
if (!(next == applied_)) {
|
||||
apply(next);
|
||||
applied_ = next;
|
||||
upcoming_dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void apply(const radar::ipc::ComboKey& combo) {
|
||||
// Set the transmitter before the receiver.
|
||||
output_switch_->switch_to(combo.output_pos);
|
||||
input_switch_->switch_to(combo.input_pos);
|
||||
settled_after_ns_ = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count()) +
|
||||
static_cast<std::uint64_t>(settings_.settle_ms * 1e6);
|
||||
}
|
||||
|
||||
Settings settings_;
|
||||
std::unique_ptr<drivers::H7992MinimalDriver> input_switch_;
|
||||
std::unique_ptr<drivers::H7992MinimalDriver> output_switch_;
|
||||
std::size_t index_ = 0;
|
||||
radar::ipc::ComboKey applied_{};
|
||||
bool upcoming_dirty_ = false;
|
||||
std::uint64_t settled_after_ns_ = 0, cycle_id_ = 1;
|
||||
};
|
||||
|
||||
} // namespace radar::rph
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class TtyProtocolWriter {
|
||||
public:
|
||||
struct StatsSnapshot {
|
||||
std::uint64_t frames_written = 0;
|
||||
std::uint64_t frames_dropped = 0;
|
||||
std::uint64_t ring_overflows = 0;
|
||||
};
|
||||
|
||||
TtyProtocolWriter(std::string path, std::size_t ring_capacity_bytes);
|
||||
~TtyProtocolWriter();
|
||||
|
||||
TtyProtocolWriter(const TtyProtocolWriter&) = delete;
|
||||
TtyProtocolWriter& operator=(const TtyProtocolWriter&) = delete;
|
||||
TtyProtocolWriter(TtyProtocolWriter&& other) noexcept = delete;
|
||||
TtyProtocolWriter& operator=(TtyProtocolWriter&& other) noexcept = delete;
|
||||
|
||||
struct SweepMetadata {
|
||||
std::uint64_t source_id, cycle_id, capture_start_ns, capture_end_ns;
|
||||
std::uint16_t input = 0xFFFF, output = 0xFFFF;
|
||||
bool dirty = false;
|
||||
};
|
||||
void enqueue_sweep(const std::vector<uint16_t>& words, const SweepMetadata& metadata);
|
||||
StatsSnapshot stats() const;
|
||||
|
||||
const std::string& path() const;
|
||||
void throw_if_failed() const;
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
void worker_loop();
|
||||
|
||||
std::string path_;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
@@ -0,0 +1,530 @@
|
||||
#include "x502api.h"
|
||||
#include "e502api.h"
|
||||
#include "tty_protocol_writer.h"
|
||||
#include "switch_sequencer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace {
|
||||
using Json = nlohmann::json;
|
||||
constexpr uint32_t kDiSyn2 = (1U << 13U) | (1U << 17U);
|
||||
constexpr uint32_t kDi8 = 1U << 6U; // Hardware DI8 readback offset, verified by loopback.
|
||||
constexpr uint32_t kDo8 = 1U << 7U;
|
||||
constexpr uint32_t kBlockWords = 65536;
|
||||
|
||||
[[noreturn]] void fail(const std::string& message) { throw std::runtime_error(message); }
|
||||
|
||||
void bind_to_parent() {
|
||||
const auto parent = ::getppid();
|
||||
if (::prctl(PR_SET_PDEATHSIG, SIGKILL) != 0 || ::getppid() != parent || parent == 1) {
|
||||
fail("Collector must remain owned by its launching application");
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t monotonic_ns() {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
struct Config {
|
||||
std::string tty_path;
|
||||
Json switching;
|
||||
uint32_t range = X502_ADC_RANGE_2;
|
||||
uint32_t reference_hz = X502_REF_FREQ_2000KHZ;
|
||||
uint32_t do8_period = 8;
|
||||
double duration_ms = 100;
|
||||
};
|
||||
|
||||
Config parse_args(int argc, char** argv) {
|
||||
Config cfg;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string arg = argv[i];
|
||||
const auto colon = arg.find(':');
|
||||
const auto key = arg.substr(0, colon);
|
||||
const auto value = colon == std::string::npos ? "" : arg.substr(colon + 1);
|
||||
if (key == "tty") { cfg.tty_path = value; }
|
||||
else if (key == "switching") { cfg.switching = Json::parse(value); }
|
||||
else if (key == "duration_ms") {
|
||||
std::size_t consumed = 0;
|
||||
cfg.duration_ms = std::stod(value, &consumed);
|
||||
if (consumed != value.size()) { fail("Invalid duration_ms"); }
|
||||
}
|
||||
else if (key == "do8_cycle_period") {
|
||||
std::size_t consumed = 0;
|
||||
const auto period = std::stoull(value, &consumed);
|
||||
if (consumed != value.size() || period < 4 || period > 262144 ||
|
||||
std::lcm(std::uint64_t{4}, period) > 262144) {
|
||||
fail("Combined DO1+DO8 cyclic pattern exceeds 262144 words or has an invalid period");
|
||||
}
|
||||
cfg.do8_period = static_cast<uint32_t>(period);
|
||||
} else if (key == "internal_ref_hz") {
|
||||
if (value == "2000000") { cfg.reference_hz = X502_REF_FREQ_2000KHZ; }
|
||||
else if (value == "1500000") { cfg.reference_hz = X502_REF_FREQ_1500KHZ; }
|
||||
else { fail("internal_ref_hz must be 1500000 or 2000000"); }
|
||||
} else if (key == "range") {
|
||||
const std::array<std::string, 6> ranges{"10", "5", "2", "1", "0.5", "0.2"};
|
||||
const auto found = std::find(ranges.begin(), ranges.end(), value);
|
||||
if (found == ranges.end()) { fail("Unsupported ADC range: " + value); }
|
||||
cfg.range = static_cast<uint32_t>(found - ranges.begin());
|
||||
} else if (arg != "profile:phase" && arg != "clock:internal" &&
|
||||
arg != "start:di_syn2_rise" && arg != "stop:di_syn2_fall" &&
|
||||
arg != "sample_clock_hz:max" && arg != "packet_limit:0" &&
|
||||
arg != "do1_toggle_per_frame" && arg != "do1_pair_subtract_avg" && arg != "do8_freq_ref") {
|
||||
fail("Unsupported application collector option: " + arg);
|
||||
}
|
||||
}
|
||||
if (cfg.tty_path.empty()) { fail("Application must supply tty:<path>"); }
|
||||
if (!std::isfinite(cfg.duration_ms) || cfg.duration_ms <= 0 || cfg.duration_ms > 60000) {
|
||||
fail("duration_ms must be finite and in (0, 60000]");
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void* open_library(const char* name) {
|
||||
auto* handle = ::dlopen(name, RTLD_NOW | RTLD_LOCAL);
|
||||
if (!handle) { fail(std::string("Cannot load ") + name + ": " + ::dlerror()); }
|
||||
return handle;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
Fn symbol(void* library, const char* name) {
|
||||
auto fn = reinterpret_cast<Fn>(::dlsym(library, name));
|
||||
if (!fn) { fail(std::string("Missing L-Card SDK symbol: ") + name); }
|
||||
return fn;
|
||||
}
|
||||
|
||||
struct Api {
|
||||
void* x502 = open_library("libx502api.so");
|
||||
void* e502 = open_library("libe502api.so");
|
||||
decltype(&X502_Create) Create = symbol<decltype(Create)>(x502, "X502_Create");
|
||||
decltype(&X502_Free) Free = symbol<decltype(Free)>(x502, "X502_Free");
|
||||
decltype(&X502_Close) Close = symbol<decltype(Close)>(x502, "X502_Close");
|
||||
decltype(&X502_GetErrorString) GetErrorString = symbol<decltype(GetErrorString)>(x502, "X502_GetErrorString");
|
||||
decltype(&X502_SetMode) SetMode = symbol<decltype(SetMode)>(x502, "X502_SetMode");
|
||||
decltype(&X502_StreamsStop) StreamsStop = symbol<decltype(StreamsStop)>(x502, "X502_StreamsStop");
|
||||
decltype(&X502_StreamsDisable) StreamsDisable = symbol<decltype(StreamsDisable)>(x502, "X502_StreamsDisable");
|
||||
decltype(&X502_SetSyncMode) SetSyncMode = symbol<decltype(SetSyncMode)>(x502, "X502_SetSyncMode");
|
||||
decltype(&X502_SetSyncStartMode) SetSyncStartMode = symbol<decltype(SetSyncStartMode)>(x502, "X502_SetSyncStartMode");
|
||||
decltype(&X502_SetLChannelCount) SetLChannelCount = symbol<decltype(SetLChannelCount)>(x502, "X502_SetLChannelCount");
|
||||
decltype(&X502_SetLChannel) SetLChannel = symbol<decltype(SetLChannel)>(x502, "X502_SetLChannel");
|
||||
decltype(&X502_SetAdcFreq) SetAdcFreq = symbol<decltype(SetAdcFreq)>(x502, "X502_SetAdcFreq");
|
||||
decltype(&X502_GetAdcFreq) GetAdcFreq = symbol<decltype(GetAdcFreq)>(x502, "X502_GetAdcFreq");
|
||||
decltype(&X502_SetDinFreq) SetDinFreq = symbol<decltype(SetDinFreq)>(x502, "X502_SetDinFreq");
|
||||
decltype(&X502_SetOutFreq) SetOutFreq = symbol<decltype(SetOutFreq)>(x502, "X502_SetOutFreq");
|
||||
decltype(&X502_AsyncOutDig) AsyncOutDig = symbol<decltype(AsyncOutDig)>(x502, "X502_AsyncOutDig");
|
||||
decltype(&X502_SetRefFreq) SetRefFreq = symbol<decltype(SetRefFreq)>(x502, "X502_SetRefFreq");
|
||||
decltype(&X502_SetStreamBufSize) SetStreamBufSize = symbol<decltype(SetStreamBufSize)>(x502, "X502_SetStreamBufSize");
|
||||
decltype(&X502_SetStreamStep) SetStreamStep = symbol<decltype(SetStreamStep)>(x502, "X502_SetStreamStep");
|
||||
decltype(&X502_SetDigInPullup) SetDigInPullup = symbol<decltype(SetDigInPullup)>(x502, "X502_SetDigInPullup");
|
||||
decltype(&X502_Configure) Configure = symbol<decltype(Configure)>(x502, "X502_Configure");
|
||||
decltype(&X502_StreamsEnable) StreamsEnable = symbol<decltype(StreamsEnable)>(x502, "X502_StreamsEnable");
|
||||
decltype(&X502_StreamsStart) StreamsStart = symbol<decltype(StreamsStart)>(x502, "X502_StreamsStart");
|
||||
decltype(&X502_GetRecvReadyCount) GetRecvReadyCount = symbol<decltype(GetRecvReadyCount)>(x502, "X502_GetRecvReadyCount");
|
||||
decltype(&X502_Recv) Recv = symbol<decltype(Recv)>(x502, "X502_Recv");
|
||||
decltype(&X502_OutCycleLoadStart) OutCycleLoadStart = symbol<decltype(OutCycleLoadStart)>(x502, "X502_OutCycleLoadStart");
|
||||
decltype(&X502_OutCycleSetup) OutCycleSetup = symbol<decltype(OutCycleSetup)>(x502, "X502_OutCycleSetup");
|
||||
decltype(&X502_OutCycleStop) OutCycleStop = symbol<decltype(OutCycleStop)>(x502, "X502_OutCycleStop");
|
||||
decltype(&X502_Send) Send = symbol<decltype(Send)>(x502, "X502_Send");
|
||||
decltype(&X502_PrepareData) PrepareData = symbol<decltype(PrepareData)>(x502, "X502_PrepareData");
|
||||
decltype(&X502_ProcessData) ProcessData = symbol<decltype(ProcessData)>(x502, "X502_ProcessData");
|
||||
decltype(&E502_OpenUsb) OpenUsb = symbol<decltype(OpenUsb)>(e502, "E502_OpenUsb");
|
||||
~Api() { ::dlclose(e502); ::dlclose(x502); }
|
||||
};
|
||||
|
||||
void expect_ok(const Api& api, int32_t error, const char* operation) {
|
||||
if (error != X502_ERR_OK) {
|
||||
const char* description = api.GetErrorString(error);
|
||||
fail(std::string(operation) + ": " + std::to_string(error) +
|
||||
(description ? std::string(" (") + description + ")" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceHandle {
|
||||
const Api& api;
|
||||
t_x502_hnd hnd;
|
||||
bool opened = false;
|
||||
bool streams_started = false;
|
||||
explicit DeviceHandle(const Api& sdk) : api(sdk), hnd(api.Create()) {
|
||||
if (!hnd) { fail("X502_Create failed"); }
|
||||
}
|
||||
~DeviceHandle() {
|
||||
if (streams_started) { api.OutCycleStop(hnd, 0); api.StreamsStop(hnd); }
|
||||
if (opened) { api.Close(hnd); }
|
||||
api.Free(hnd);
|
||||
}
|
||||
};
|
||||
|
||||
volatile std::sig_atomic_t stop_requested = 0;
|
||||
void stop_handler(int signal) {
|
||||
if (stop_requested) { std::_Exit(128 + signal); }
|
||||
stop_requested = 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<radar::rph::SwitchSequencer> make_switch_sequencer(const Config& cfg) {
|
||||
if (cfg.switching.is_null()) { return nullptr; }
|
||||
const auto driver = [](const Json& item) {
|
||||
if (item.value("driver", std::string("h7992")) != "h7992" || item.value("invert_logic", false)) {
|
||||
fail("Switches support only h7992 with invert_logic=false");
|
||||
}
|
||||
const auto mode = item.at("driver_mode").get<std::string>();
|
||||
if (mode != "native" && mode != "mock") { fail("Invalid switch driver_mode"); }
|
||||
return std::make_unique<radar::drivers::H7992MinimalDriver>(radar::drivers::H7992MinimalDriverSettings{
|
||||
.name = item.at("name").get<std::string>(),
|
||||
.mode = mode == "native" ? radar::config::DriverMode::Native : radar::config::DriverMode::Mock,
|
||||
.positions = item.at("positions").get<uint32_t>(),
|
||||
.default_position = item.at("default_position").get<uint32_t>(),
|
||||
.gpio_chip = item.at("gpio_chip").get<std::string>(),
|
||||
.pin_a = item.at("pin_a").get<int32_t>(), .pin_b = item.at("pin_b").get<int32_t>()});
|
||||
};
|
||||
radar::rph::SwitchSequencer::Settings settings;
|
||||
settings.settle_ms = cfg.switching.at("settling_ms").get<double>();
|
||||
if (!std::isfinite(settings.settle_ms) || settings.settle_ms < 0 || settings.settle_ms > 60000) {
|
||||
fail("Switch settling_ms must be finite and in [0, 60000]");
|
||||
}
|
||||
settings.settle_ms = std::max(1.0, settings.settle_ms);
|
||||
const auto& input = cfg.switching.at("input");
|
||||
const auto& output = cfg.switching.at("output");
|
||||
for (const auto& pair : cfg.switching.at("combos")) {
|
||||
if (!pair.is_array() || pair.size() != 2 || !pair[0].is_number_unsigned() || !pair[1].is_number_unsigned() ||
|
||||
pair[0].get<uint64_t>() >= input.at("positions").get<uint64_t>() ||
|
||||
pair[1].get<uint64_t>() >= output.at("positions").get<uint64_t>()) {
|
||||
fail("Switch combination out of range");
|
||||
}
|
||||
settings.combos.push_back({pair[0].get<uint32_t>(), pair[1].get<uint32_t>()});
|
||||
}
|
||||
if (settings.combos.empty()) { fail("Collector switching requires nonempty combos"); }
|
||||
return std::make_unique<radar::rph::SwitchSequencer>(std::move(settings), driver(input), driver(output));
|
||||
}
|
||||
|
||||
int16_t pack_raw_code_to_int16(double value) {
|
||||
const auto rounded = std::llround(value * 32767.0 / static_cast<double>(X502_ADC_SCALE_CODE_MAX));
|
||||
return static_cast<int16_t>(std::clamp<long long>(rounded, -32768, 32767));
|
||||
}
|
||||
struct Do1PairSubtractAverageState {
|
||||
struct PairSample {
|
||||
double ch1 = 0.0;
|
||||
double ch2 = 0.0;
|
||||
};
|
||||
|
||||
std::deque<PairSample> low_pairs;
|
||||
std::deque<PairSample> high_pairs;
|
||||
bool run_initialized = false;
|
||||
bool run_di1_high = false;
|
||||
double diff_sum_ch1 = 0.0;
|
||||
double diff_sum_ch2 = 0.0;
|
||||
uint32_t diff_count = 0;
|
||||
|
||||
bool pending_ch1_valid = false;
|
||||
double pending_ch1_raw = 0.0;
|
||||
bool pending_ch1_di1_high = false;
|
||||
bool pending_ch1_di2_high = false;
|
||||
bool pending_ch1_di8_high = false; // raw DI8 level (not guard-filtered)
|
||||
|
||||
uint16_t next_index = 1;
|
||||
|
||||
void clear_run_pairs() {
|
||||
low_pairs.clear();
|
||||
high_pairs.clear();
|
||||
diff_sum_ch1 = 0.0;
|
||||
diff_sum_ch2 = 0.0;
|
||||
diff_count = 0;
|
||||
}
|
||||
|
||||
void clear_pending_ch1() {
|
||||
pending_ch1_valid = false;
|
||||
pending_ch1_raw = 0.0;
|
||||
pending_ch1_di1_high = false;
|
||||
pending_ch1_di2_high = false;
|
||||
pending_ch1_di8_high = false;
|
||||
}
|
||||
|
||||
void reset_packet() {
|
||||
clear_run_pairs();
|
||||
run_initialized = false;
|
||||
run_di1_high = false;
|
||||
clear_pending_ch1();
|
||||
next_index = 1;
|
||||
}
|
||||
|
||||
void start_run(bool di1_high) {
|
||||
clear_run_pairs();
|
||||
run_initialized = true;
|
||||
run_di1_high = di1_high;
|
||||
}
|
||||
|
||||
void clear_run() {
|
||||
clear_run_pairs();
|
||||
run_initialized = false;
|
||||
}
|
||||
|
||||
void add_pending_ch1(double raw_code, bool di1_high, bool di2_high,
|
||||
bool di8_high_level = false) {
|
||||
pending_ch1_valid = true;
|
||||
pending_ch1_raw = raw_code;
|
||||
pending_ch1_di1_high = di1_high;
|
||||
pending_ch1_di2_high = di2_high;
|
||||
pending_ch1_di8_high = di8_high_level;
|
||||
}
|
||||
|
||||
void add_pair_sample(bool di2_high, double ch1_value, double ch2_value) {
|
||||
const PairSample sample{ch1_value, ch2_value};
|
||||
if (di2_high) {
|
||||
high_pairs.push_back(sample);
|
||||
} else {
|
||||
low_pairs.push_back(sample);
|
||||
}
|
||||
|
||||
while (!low_pairs.empty() && !high_pairs.empty()) {
|
||||
const PairSample low = low_pairs.front();
|
||||
const PairSample high = high_pairs.front();
|
||||
low_pairs.pop_front();
|
||||
high_pairs.pop_front();
|
||||
|
||||
diff_sum_ch1 += (low.ch1 - high.ch1);
|
||||
diff_sum_ch2 += (low.ch2 - high.ch2);
|
||||
++diff_count;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_output() const { return diff_count != 0U; }
|
||||
|
||||
double avg_diff_ch1() const {
|
||||
if (diff_count == 0U) {
|
||||
return 0.0;
|
||||
}
|
||||
return diff_sum_ch1 / static_cast<double>(diff_count);
|
||||
}
|
||||
|
||||
double avg_diff_ch2() const {
|
||||
if (diff_count == 0U) {
|
||||
return 0.0;
|
||||
}
|
||||
return diff_sum_ch2 / static_cast<double>(diff_count);
|
||||
}
|
||||
};
|
||||
|
||||
int run(const Config& cfg) {
|
||||
stop_requested = 0;
|
||||
struct sigaction action{};
|
||||
action.sa_handler = stop_handler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
if (::sigaction(SIGINT, &action, nullptr) != 0 || ::sigaction(SIGTERM, &action, nullptr) != 0) {
|
||||
fail("Cannot install collector stop handlers");
|
||||
}
|
||||
Api api;
|
||||
auto sequencer = make_switch_sequencer(cfg);
|
||||
if (sequencer) { sequencer->open(); }
|
||||
std::unique_ptr<DeviceHandle> device;
|
||||
bool restart_requested = true;
|
||||
double adc_hz = 0;
|
||||
uint64_t epoch_ns = 0, max_frames = 0;
|
||||
TtyProtocolWriter writer(cfg.tty_path, 2U * (48U + 65534U * 16U));
|
||||
std::vector<uint32_t> raw(kBlockWords), din(kBlockWords);
|
||||
std::vector<double> adc(kBlockWords);
|
||||
std::deque<double> pending_adc;
|
||||
std::deque<uint32_t> pending_din;
|
||||
std::vector<uint16_t> words;
|
||||
Do1PairSubtractAverageState pairs;
|
||||
std::array<double, 2> reference_sum{};
|
||||
uint32_t reference_count = 0;
|
||||
bool active = false, previous_gate = false, initialized = false;
|
||||
uint64_t sample_index = 0, sweep_id = 0, frames = 0;
|
||||
TtyProtocolWriter::SweepMetadata metadata{};
|
||||
const auto append = [&](uint16_t marker, uint16_t step, double i, double q) {
|
||||
words.insert(words.end(), {marker, step, static_cast<uint16_t>(pack_raw_code_to_int16(i)),
|
||||
static_cast<uint16_t>(pack_raw_code_to_int16(q))});
|
||||
};
|
||||
const auto finish_step = [&]() {
|
||||
if (pairs.run_initialized && pairs.has_output()) {
|
||||
if (pairs.next_index == 0xffff) { fail("Sweep exceeds 65534 phase points"); }
|
||||
const auto step = pairs.next_index++;
|
||||
append(0x000a, step, pairs.avg_diff_ch1(), pairs.avg_diff_ch2());
|
||||
if (reference_count) {
|
||||
append(0x00a8, step, reference_sum[0] / reference_count, reference_sum[1] / reference_count);
|
||||
}
|
||||
}
|
||||
pairs.clear_run();
|
||||
reference_sum = {};
|
||||
reference_count = 0;
|
||||
};
|
||||
const auto finish_sweep = [&](uint64_t end_ns, bool complete) {
|
||||
finish_step();
|
||||
metadata.capture_end_ns = end_ns;
|
||||
metadata.dirty = metadata.dirty || !complete;
|
||||
if (!words.empty() && end_ns > metadata.capture_start_ns) { writer.enqueue_sweep(words, metadata); }
|
||||
if (sequencer && frames && !metadata.dirty) { restart_requested = sequencer->on_sweep_end(complete); }
|
||||
words.clear();
|
||||
active = false;
|
||||
};
|
||||
const auto stop_session = [&]() {
|
||||
if (!device) { return; }
|
||||
expect_ok(api, api.OutCycleStop(device->hnd, X502_OUT_CYCLE_FLAGS_WAIT_DONE), "Stop DOUT cycle");
|
||||
expect_ok(api, api.StreamsStop(device->hnd), "Stop streams");
|
||||
device->streams_started = false;
|
||||
expect_ok(api, api.StreamsDisable(device->hnd, X502_STREAM_ALL_IN | X502_STREAM_ALL_OUT), "Disable streams");
|
||||
expect_ok(api, api.AsyncOutDig(device->hnd, 2, ~(3U | kDo8)), "Restore DO2 high");
|
||||
expect_ok(api, api.Close(device->hnd), "Close ADC session");
|
||||
device->opened = false;
|
||||
device.reset(); // X502_Free destroys this session's decoder/DMA buffers.
|
||||
};
|
||||
const auto start_session = [&]() {
|
||||
stop_session();
|
||||
pending_adc.clear(); pending_din.clear(); words.clear();
|
||||
pairs.reset_packet(); reference_sum = {}; reference_count = 0;
|
||||
active = false; previous_gate = false; initialized = false;
|
||||
sample_index = 0; frames = 0;
|
||||
while (sequencer && monotonic_ns() < sequencer->settled_after_ns() && !stop_requested) { ::usleep(1000); }
|
||||
if (stop_requested) { return; }
|
||||
device = std::make_unique<DeviceHandle>(api);
|
||||
expect_ok(api, api.OpenUsb(device->hnd, nullptr), "Open E-502");
|
||||
device->opened = true;
|
||||
expect_ok(api, api.SetMode(device->hnd, X502_MODE_FPGA), "Set FPGA mode");
|
||||
expect_ok(api, api.StreamsStop(device->hnd), "Stop prior streams");
|
||||
expect_ok(api, api.StreamsDisable(device->hnd, X502_STREAM_ALL_IN | X502_STREAM_ALL_OUT), "Disable prior streams");
|
||||
expect_ok(api, api.SetSyncMode(device->hnd, X502_SYNC_INTERNAL), "Set internal sample clock");
|
||||
expect_ok(api, api.SetSyncStartMode(device->hnd, X502_SYNC_INTERNAL), "Set immediate start");
|
||||
expect_ok(api, api.SetRefFreq(device->hnd, cfg.reference_hz), "Set reference clock");
|
||||
expect_ok(api, api.SetLChannelCount(device->hnd, 2), "Set logical channels");
|
||||
for (uint32_t ch = 0; ch < 2; ++ch) {
|
||||
expect_ok(api, api.SetLChannel(device->hnd, ch, ch + 2, X502_LCH_MODE_DIFF, cfg.range, 1), "Set ADC channel");
|
||||
}
|
||||
adc_hz = cfg.reference_hz;
|
||||
double frame_hz = 0;
|
||||
expect_ok(api, api.SetAdcFreq(device->hnd, &adc_hz, nullptr), "Set maximum ADC frequency");
|
||||
expect_ok(api, api.GetAdcFreq(device->hnd, &adc_hz, &frame_hz), "Get ADC frequencies");
|
||||
double din_hz = adc_hz, dout_hz = adc_hz;
|
||||
expect_ok(api, api.SetDinFreq(device->hnd, &din_hz), "Set DIN frequency");
|
||||
expect_ok(api, api.SetOutFreq(device->hnd, &dout_hz), "Set DOUT frequency");
|
||||
if (!std::isfinite(adc_hz) || adc_hz <= 0 || !std::isfinite(frame_hz) || frame_hz <= 0 ||
|
||||
!std::isfinite(din_hz) || !std::isfinite(dout_hz) || adc_hz > cfg.reference_hz ||
|
||||
std::abs(frame_hz * 2 - adc_hz) > 0.5 || std::abs(din_hz - adc_hz) > 0.5 || std::abs(dout_hz - adc_hz) > 0.5) {
|
||||
fail("ADC, DIN and DOUT must have the same finite sample frequency");
|
||||
}
|
||||
for (const auto stream : {X502_STREAM_CH_IN, X502_STREAM_CH_OUT}) {
|
||||
expect_ok(api, api.SetStreamBufSize(device->hnd, stream, 16U * 1024U * 1024U), "Set stream buffer");
|
||||
expect_ok(api, api.SetStreamStep(device->hnd, stream, kBlockWords), "Set stream step");
|
||||
}
|
||||
expect_ok(api, api.SetDigInPullup(device->hnd, 0), "Set input pulls");
|
||||
expect_ok(api, api.Configure(device->hnd, 0), "Configure E-502");
|
||||
expect_ok(api, api.AsyncOutDig(device->hnd, 2, 0), "Set DO2 high");
|
||||
expect_ok(api, api.StreamsEnable(device->hnd, X502_STREAM_ADC | X502_STREAM_DIN | X502_STREAM_DOUT), "Enable streams");
|
||||
|
||||
// Parse bounds were checked before loading the SDK or allocating either vector.
|
||||
const auto pattern_size = std::lcm(uint32_t{4}, cfg.do8_period);
|
||||
std::vector<uint32_t> pattern(pattern_size), encoded(pattern_size);
|
||||
for (std::size_t i = 0; i < pattern.size(); ++i) {
|
||||
pattern[i] = 2U | (i % 4U >= 2U ? 1U : 0U) |
|
||||
(i % cfg.do8_period >= cfg.do8_period - 2U ? kDo8 : 0U);
|
||||
}
|
||||
expect_ok(api, api.OutCycleLoadStart(device->hnd, pattern_size), "Allocate DOUT cycle");
|
||||
expect_ok(api, api.PrepareData(device->hnd, nullptr, nullptr, pattern.data(), pattern_size, 0, encoded.data()), "Encode DOUT cycle");
|
||||
for (std::size_t sent = 0; sent < encoded.size();) {
|
||||
const auto n = api.Send(device->hnd, encoded.data() + sent, static_cast<uint32_t>(encoded.size() - sent), 50);
|
||||
if (n <= 0) { fail("Send DOUT cycle failed or timed out: " + std::to_string(n)); }
|
||||
sent += static_cast<std::size_t>(n);
|
||||
}
|
||||
expect_ok(api, api.OutCycleSetup(device->hnd, X502_OUT_CYCLE_FLAGS_WAIT_DONE), "Activate DOUT cycle");
|
||||
max_frames = std::max<uint64_t>(1, static_cast<uint64_t>(std::llround(cfg.duration_ms * frame_hz / 1000.0)));
|
||||
// Diagnostic source-clock estimate only. Freshness follows the new SDK
|
||||
// session after settled GPIO, independently of either clock's drift.
|
||||
epoch_ns = monotonic_ns();
|
||||
expect_ok(api, api.StreamsStart(device->hnd), "Start ADC/DIN/DOUT streams");
|
||||
device->streams_started = true;
|
||||
if (sequencer) { sequencer->session_started(); }
|
||||
restart_requested = false;
|
||||
};
|
||||
while (!stop_requested) {
|
||||
if (restart_requested) { start_session(); }
|
||||
if (stop_requested) { break; }
|
||||
writer.throw_if_failed();
|
||||
uint32_t ready = 0;
|
||||
expect_ok(api, api.GetRecvReadyCount(device->hnd, &ready), "Read ADC backlog");
|
||||
const auto count = api.Recv(device->hnd, raw.data(), ready ? std::min(ready, kBlockWords) : kBlockWords, ready ? 0 : 50);
|
||||
if (count < 0) { expect_ok(api, count, "Receive ADC/DIN"); }
|
||||
if (count == 0) { continue; }
|
||||
uint32_t adc_count = kBlockWords, din_count = kBlockWords;
|
||||
expect_ok(api, api.ProcessData(device->hnd, raw.data(), static_cast<uint32_t>(count), 0,
|
||||
adc.data(), &adc_count, din.data(), &din_count), "Decode ADC/DIN");
|
||||
pending_adc.insert(pending_adc.end(), adc.begin(), adc.begin() + adc_count);
|
||||
pending_din.insert(pending_din.end(), din.begin(), din.begin() + din_count);
|
||||
if (pending_adc.size() > 1'000'000 || pending_din.size() > 1'000'000) {
|
||||
fail("ADC/DIN alignment backlog exceeded its bound");
|
||||
}
|
||||
const auto received_at_ns = monotonic_ns();
|
||||
while (!pending_adc.empty() && !pending_din.empty() && !stop_requested && !restart_requested) {
|
||||
const auto value = pending_adc.front(); pending_adc.pop_front();
|
||||
const auto bits = pending_din.front(); pending_din.pop_front();
|
||||
const auto at_ns = epoch_ns + static_cast<uint64_t>(static_cast<long double>(sample_index) * 1e9L / adc_hz);
|
||||
if (at_ns > received_at_ns) { fail("ADC source clock leads host monotonic clock; restart acquisition"); }
|
||||
const auto channel = sample_index++ % 2U;
|
||||
const bool gate = (bits & kDiSyn2) != 0;
|
||||
if (!initialized || (!previous_gate && gate)) {
|
||||
if (gate) {
|
||||
pairs.reset_packet(); reference_sum = {}; reference_count = 0;
|
||||
words.clear(); frames = 0; ++sweep_id;
|
||||
metadata = {sweep_id, sequencer ? sequencer->cycle_id() : sweep_id, at_ns, at_ns,
|
||||
0xffff, 0xffff, !initialized};
|
||||
if (sequencer) {
|
||||
const auto combo = sequencer->current_combo();
|
||||
metadata.input = static_cast<uint16_t>(combo.input_pos);
|
||||
metadata.output = static_cast<uint16_t>(combo.output_pos);
|
||||
metadata.dirty = metadata.dirty || sequencer->current_dirty();
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
if (active && previous_gate && !gate) { finish_sweep(at_ns, true); }
|
||||
previous_gate = gate;
|
||||
if (!active) { continue; }
|
||||
const bool di1 = (bits & 1U) != 0, di2 = (bits & 2U) != 0, di8 = (bits & kDi8) != 0;
|
||||
if (channel == 0) {
|
||||
pairs.add_pending_ch1(value, di1, di2, di8);
|
||||
} else {
|
||||
if (pairs.pending_ch1_valid && pairs.pending_ch1_di1_high == di1 && pairs.pending_ch1_di2_high == di2) {
|
||||
if (pairs.pending_ch1_di8_high && di8) {
|
||||
reference_sum[0] += pairs.pending_ch1_raw;
|
||||
reference_sum[1] += value;
|
||||
++reference_count;
|
||||
} else if (!pairs.pending_ch1_di8_high && !di8) {
|
||||
if (!pairs.run_initialized) { pairs.start_run(di1); }
|
||||
else if (pairs.run_di1_high != di1) { finish_step(); pairs.start_run(di1); }
|
||||
pairs.add_pair_sample(di2, pairs.pending_ch1_raw, value);
|
||||
}
|
||||
}
|
||||
pairs.clear_pending_ch1();
|
||||
if (++frames >= max_frames) { finish_sweep(at_ns, false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stop discards the incomplete current sweep. It never advances the switches.
|
||||
writer.shutdown();
|
||||
stop_session();
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
bind_to_parent();
|
||||
return run(parse_args(argc, argv));
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "Collector error: " << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
#include "tty_protocol_writer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cerrno>
|
||||
#include <condition_variable>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <pty.h>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::string io_error(const std::string& action, const std::string& path) {
|
||||
std::ostringstream out;
|
||||
out << action << " '" << path << "': " << std::strerror(errno);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void close_fd_if_open(int& fd) noexcept {
|
||||
if (fd >= 0) {
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void set_fd_raw(int fd) {
|
||||
struct termios tio {};
|
||||
if (::tcgetattr(fd, &tio) != 0) {
|
||||
throw std::runtime_error(io_error("Cannot read tty attributes for", std::to_string(fd)));
|
||||
}
|
||||
::cfmakeraw(&tio);
|
||||
tio.c_cc[VMIN] = 1;
|
||||
tio.c_cc[VTIME] = 0;
|
||||
if (::tcsetattr(fd, TCSANOW, &tio) != 0) {
|
||||
throw std::runtime_error(io_error("Cannot apply raw tty attributes to", std::to_string(fd)));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> read_link_target(const std::string& path) {
|
||||
std::array<char, PATH_MAX> buf {};
|
||||
const ssize_t len = ::readlink(path.c_str(), buf.data(), buf.size() - 1U);
|
||||
if (len < 0) {
|
||||
if (errno == EINVAL || errno == ENOENT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
throw std::runtime_error(io_error("Cannot read symlink", path));
|
||||
}
|
||||
buf[static_cast<std::size_t>(len)] = '\0';
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct TtyProtocolWriter::Impl {
|
||||
explicit Impl(std::size_t bytes) : capacity_bytes(bytes) {}
|
||||
|
||||
int fd = -1;
|
||||
int slave_fd = -1;
|
||||
std::string slave_path;
|
||||
bool owns_link = false;
|
||||
const std::size_t capacity_bytes;
|
||||
std::deque<std::vector<std::uint8_t>> packets;
|
||||
std::size_t queued_bytes = 0;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
std::condition_variable data_ready_cv;
|
||||
std::thread worker;
|
||||
bool stop_requested = false;
|
||||
std::exception_ptr failure;
|
||||
StatsSnapshot stats;
|
||||
};
|
||||
|
||||
TtyProtocolWriter::TtyProtocolWriter(std::string path, std::size_t ring_capacity_bytes)
|
||||
: path_(std::move(path)),
|
||||
impl_(std::make_unique<Impl>(ring_capacity_bytes)) {
|
||||
std::array<char, PATH_MAX> slave_name {};
|
||||
if (::openpty(&impl_->fd, &impl_->slave_fd, slave_name.data(), nullptr, nullptr) != 0) {
|
||||
throw std::runtime_error(io_error("Cannot create PTY bridge for", path_));
|
||||
}
|
||||
|
||||
try {
|
||||
impl_->slave_path = slave_name.data();
|
||||
set_fd_raw(impl_->slave_fd);
|
||||
|
||||
struct stat st {};
|
||||
if (::lstat(path_.c_str(), &st) == 0) {
|
||||
if (!S_ISLNK(st.st_mode) && !S_ISREG(st.st_mode)) {
|
||||
throw std::runtime_error("Refusing to replace non-link path '" + path_ + "'");
|
||||
}
|
||||
if (::unlink(path_.c_str()) != 0) {
|
||||
throw std::runtime_error(io_error("Cannot remove existing tty link", path_));
|
||||
}
|
||||
} else if (errno != ENOENT) {
|
||||
throw std::runtime_error(io_error("Cannot inspect tty link path", path_));
|
||||
}
|
||||
|
||||
if (::symlink(impl_->slave_path.c_str(), path_.c_str()) != 0) {
|
||||
throw std::runtime_error(io_error("Cannot create tty symlink", path_));
|
||||
}
|
||||
impl_->owns_link = true;
|
||||
} catch (...) {
|
||||
close_fd_if_open(impl_->slave_fd);
|
||||
close_fd_if_open(impl_->fd);
|
||||
throw;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto flags = ::fcntl(impl_->fd, F_GETFL);
|
||||
if (flags < 0 || ::fcntl(impl_->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
throw std::runtime_error(io_error("Cannot make tty output nonblocking", path_));
|
||||
}
|
||||
impl_->worker = std::thread([this]() { worker_loop(); });
|
||||
} catch (...) {
|
||||
if (impl_->owns_link) {
|
||||
::unlink(path_.c_str());
|
||||
}
|
||||
close_fd_if_open(impl_->slave_fd);
|
||||
close_fd_if_open(impl_->fd);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
TtyProtocolWriter::~TtyProtocolWriter() {
|
||||
try {
|
||||
shutdown();
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
if (!impl_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (impl_->owns_link && !path_.empty()) {
|
||||
try {
|
||||
const auto target = read_link_target(path_);
|
||||
if (target && (*target == impl_->slave_path)) {
|
||||
::unlink(path_.c_str());
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
impl_->owns_link = false;
|
||||
}
|
||||
|
||||
close_fd_if_open(impl_->slave_fd);
|
||||
close_fd_if_open(impl_->fd);
|
||||
}
|
||||
|
||||
void TtyProtocolWriter::enqueue_sweep(const std::vector<uint16_t>& words, const SweepMetadata& metadata) {
|
||||
if (words.empty()) { return; }
|
||||
if (words.size() % 4U != 0U || words.size() > 65534U * 8U ||
|
||||
metadata.source_id == 0 || metadata.cycle_id == 0 ||
|
||||
metadata.capture_start_ns == 0 || metadata.capture_end_ns <= metadata.capture_start_ns) {
|
||||
throw std::runtime_error("Invalid RPH sweep packet");
|
||||
}
|
||||
std::vector<std::uint8_t> packet{'R', 'P', 'H', '1'};
|
||||
packet.reserve(48U + words.size() * 2U);
|
||||
const auto append = [&](std::uint64_t value, unsigned bytes) {
|
||||
for (unsigned i = 0; i < bytes; ++i) { packet.push_back(static_cast<std::uint8_t>(value >> (i * 8U))); }
|
||||
};
|
||||
append(words.size() * 2U, 4);
|
||||
append(metadata.source_id, 8);
|
||||
append(metadata.cycle_id, 8);
|
||||
append(metadata.capture_start_ns, 8);
|
||||
append(metadata.capture_end_ns, 8);
|
||||
append(metadata.input, 2);
|
||||
append(metadata.output, 2);
|
||||
append(metadata.dirty, 4);
|
||||
for (const auto word : words) { append(word, 2); }
|
||||
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (impl_->failure) { std::rethrow_exception(impl_->failure); }
|
||||
if (impl_->stop_requested) { throw std::runtime_error("tty writer is already shut down"); }
|
||||
if (packet.size() > impl_->capacity_bytes) {
|
||||
throw std::runtime_error("RPH sweep exceeds tty queue capacity");
|
||||
}
|
||||
while (impl_->queued_bytes + packet.size() > impl_->capacity_bytes) {
|
||||
impl_->stats.frames_dropped += (impl_->packets.front().size() - 48U) / 8U;
|
||||
impl_->queued_bytes -= impl_->packets.front().size();
|
||||
impl_->packets.pop_front();
|
||||
++impl_->stats.ring_overflows;
|
||||
}
|
||||
impl_->queued_bytes += packet.size();
|
||||
impl_->packets.push_back(std::move(packet));
|
||||
impl_->data_ready_cv.notify_one();
|
||||
}
|
||||
|
||||
TtyProtocolWriter::StatsSnapshot TtyProtocolWriter::stats() const {
|
||||
if (!impl_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
return impl_->stats;
|
||||
}
|
||||
|
||||
const std::string& TtyProtocolWriter::path() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
void TtyProtocolWriter::throw_if_failed() const {
|
||||
if (!impl_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::exception_ptr failure;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
failure = impl_->failure;
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
std::rethrow_exception(failure);
|
||||
}
|
||||
}
|
||||
|
||||
void TtyProtocolWriter::shutdown() {
|
||||
if (!impl_) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
impl_->stop_requested = true;
|
||||
}
|
||||
|
||||
impl_->data_ready_cv.notify_all();
|
||||
|
||||
if (impl_->worker.joinable()) {
|
||||
impl_->worker.join();
|
||||
}
|
||||
close_fd_if_open(impl_->fd);
|
||||
}
|
||||
|
||||
void TtyProtocolWriter::worker_loop() {
|
||||
for (;;) {
|
||||
std::vector<std::uint8_t> packet;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(impl_->mutex);
|
||||
impl_->data_ready_cv.wait(lock, [this]() {
|
||||
return impl_->stop_requested || impl_->failure || !impl_->packets.empty();
|
||||
});
|
||||
if (impl_->failure || impl_->stop_requested) { return; }
|
||||
packet = std::move(impl_->packets.front());
|
||||
impl_->packets.pop_front();
|
||||
impl_->queued_bytes -= packet.size();
|
||||
}
|
||||
// Once a packet starts writing it is indivisible, even if the reader
|
||||
// stalls. Overflow drops only complete packets still in the queue.
|
||||
const std::uint8_t* bytes = packet.data();
|
||||
std::size_t remaining = packet.size();
|
||||
while (remaining != 0U) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (impl_->stop_requested) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const ssize_t written = ::write(impl_->fd, bytes, remaining);
|
||||
if (written < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EIO)) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (impl_->stop_requested) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (!impl_->stop_requested) {
|
||||
impl_->failure = std::make_exception_ptr(
|
||||
std::runtime_error(io_error("Cannot write tty frame to", path_)));
|
||||
}
|
||||
impl_->data_ready_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
if (written == 0) {
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
if (!impl_->stop_requested) {
|
||||
impl_->failure = std::make_exception_ptr(
|
||||
std::runtime_error("tty write returned 0 bytes for '" + path_ + "'"));
|
||||
}
|
||||
impl_->data_ready_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
bytes += static_cast<std::size_t>(written);
|
||||
remaining -= static_cast<std::size_t>(written);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
impl_->stats.frames_written += (packet.size() - 48U) / 8U;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,810 @@
|
||||
/* A portable stdint.h
|
||||
****************************************************************************
|
||||
* BSD License:
|
||||
****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2005-2011 Paul Hsieh
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************
|
||||
*
|
||||
* Version 0.1.12
|
||||
*
|
||||
* The ANSI C standard committee, for the C99 standard, specified the
|
||||
* inclusion of a new standard include file called stdint.h. This is
|
||||
* a very useful and long desired include file which contains several
|
||||
* very precise definitions for integer scalar types that is
|
||||
* critically important for making portable several classes of
|
||||
* applications including cryptography, hashing, variable length
|
||||
* integer libraries and so on. But for most developers its likely
|
||||
* useful just for programming sanity.
|
||||
*
|
||||
* The problem is that most compiler vendors have decided not to
|
||||
* implement the C99 standard, and the next C++ language standard
|
||||
* (which has a lot more mindshare these days) will be a long time in
|
||||
* coming and its unknown whether or not it will include stdint.h or
|
||||
* how much adoption it will have. Either way, it will be a long time
|
||||
* before all compilers come with a stdint.h and it also does nothing
|
||||
* for the extremely large number of compilers available today which
|
||||
* do not include this file, or anything comparable to it.
|
||||
*
|
||||
* So that's what this file is all about. Its an attempt to build a
|
||||
* single universal include file that works on as many platforms as
|
||||
* possible to deliver what stdint.h is supposed to. A few things
|
||||
* that should be noted about this file:
|
||||
*
|
||||
* 1) It is not guaranteed to be portable and/or present an identical
|
||||
* interface on all platforms. The extreme variability of the
|
||||
* ANSI C standard makes this an impossibility right from the
|
||||
* very get go. Its really only meant to be useful for the vast
|
||||
* majority of platforms that possess the capability of
|
||||
* implementing usefully and precisely defined, standard sized
|
||||
* integer scalars. Systems which are not intrinsically 2s
|
||||
* complement may produce invalid constants.
|
||||
*
|
||||
* 2) There is an unavoidable use of non-reserved symbols.
|
||||
*
|
||||
* 3) Other standard include files are invoked.
|
||||
*
|
||||
* 4) This file may come in conflict with future platforms that do
|
||||
* include stdint.h. The hope is that one or the other can be
|
||||
* used with no real difference.
|
||||
*
|
||||
* 5) In the current verison, if your platform can't represent
|
||||
* int32_t, int16_t and int8_t, it just dumps out with a compiler
|
||||
* error.
|
||||
*
|
||||
* 6) 64 bit integers may or may not be defined. Test for their
|
||||
* presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
|
||||
* Note that this is different from the C99 specification which
|
||||
* requires the existence of 64 bit support in the compiler. If
|
||||
* this is not defined for your platform, yet it is capable of
|
||||
* dealing with 64 bits then it is because this file has not yet
|
||||
* been extended to cover all of your system's capabilities.
|
||||
*
|
||||
* 7) (u)intptr_t may or may not be defined. Test for its presence
|
||||
* with the test: #ifdef PTRDIFF_MAX. If this is not defined
|
||||
* for your platform, then it is because this file has not yet
|
||||
* been extended to cover all of your system's capabilities, not
|
||||
* because its optional.
|
||||
*
|
||||
* 8) The following might not been defined even if your platform is
|
||||
* capable of defining it:
|
||||
*
|
||||
* WCHAR_MIN
|
||||
* WCHAR_MAX
|
||||
* (u)int64_t
|
||||
* PTRDIFF_MIN
|
||||
* PTRDIFF_MAX
|
||||
* (u)intptr_t
|
||||
*
|
||||
* 9) The following have not been defined:
|
||||
*
|
||||
* WINT_MIN
|
||||
* WINT_MAX
|
||||
*
|
||||
* 10) The criteria for defining (u)int_least(*)_t isn't clear,
|
||||
* except for systems which don't have a type that precisely
|
||||
* defined 8, 16, or 32 bit types (which this include file does
|
||||
* not support anyways). Default definitions have been given.
|
||||
*
|
||||
* 11) The criteria for defining (u)int_fast(*)_t isn't something I
|
||||
* would trust to any particular compiler vendor or the ANSI C
|
||||
* committee. It is well known that "compatible systems" are
|
||||
* commonly created that have very different performance
|
||||
* characteristics from the systems they are compatible with,
|
||||
* especially those whose vendors make both the compiler and the
|
||||
* system. Default definitions have been given, but its strongly
|
||||
* recommended that users never use these definitions for any
|
||||
* reason (they do *NOT* deliver any serious guarantee of
|
||||
* improved performance -- not in this file, nor any vendor's
|
||||
* stdint.h).
|
||||
*
|
||||
* 12) The following macros:
|
||||
*
|
||||
* PRINTF_INTMAX_MODIFIER
|
||||
* PRINTF_INT64_MODIFIER
|
||||
* PRINTF_INT32_MODIFIER
|
||||
* PRINTF_INT16_MODIFIER
|
||||
* PRINTF_LEAST64_MODIFIER
|
||||
* PRINTF_LEAST32_MODIFIER
|
||||
* PRINTF_LEAST16_MODIFIER
|
||||
* PRINTF_INTPTR_MODIFIER
|
||||
*
|
||||
* are strings which have been defined as the modifiers required
|
||||
* for the "d", "u" and "x" printf formats to correctly output
|
||||
* (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
|
||||
* (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
|
||||
* PRINTF_INTPTR_MODIFIER is not defined for some systems which
|
||||
* provide their own stdint.h. PRINTF_INT64_MODIFIER is not
|
||||
* defined if INT64_MAX is not defined. These are an extension
|
||||
* beyond what C99 specifies must be in stdint.h.
|
||||
*
|
||||
* In addition, the following macros are defined:
|
||||
*
|
||||
* PRINTF_INTMAX_HEX_WIDTH
|
||||
* PRINTF_INT64_HEX_WIDTH
|
||||
* PRINTF_INT32_HEX_WIDTH
|
||||
* PRINTF_INT16_HEX_WIDTH
|
||||
* PRINTF_INT8_HEX_WIDTH
|
||||
* PRINTF_INTMAX_DEC_WIDTH
|
||||
* PRINTF_INT64_DEC_WIDTH
|
||||
* PRINTF_INT32_DEC_WIDTH
|
||||
* PRINTF_INT16_DEC_WIDTH
|
||||
* PRINTF_INT8_DEC_WIDTH
|
||||
*
|
||||
* Which specifies the maximum number of characters required to
|
||||
* print the number of that type in either hexadecimal or decimal.
|
||||
* These are an extension beyond what C99 specifies must be in
|
||||
* stdint.h.
|
||||
*
|
||||
* Compilers tested (all with 0 warnings at their highest respective
|
||||
* settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
|
||||
* bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
|
||||
* .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
|
||||
*
|
||||
* This file should be considered a work in progress. Suggestions for
|
||||
* improvements, especially those which increase coverage are strongly
|
||||
* encouraged.
|
||||
*
|
||||
* Acknowledgements
|
||||
*
|
||||
* The following people have made significant contributions to the
|
||||
* development and testing of this file:
|
||||
*
|
||||
* Chris Howie
|
||||
* John Steele Scott
|
||||
* Dave Thorup
|
||||
* John Dill
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LCARD_PSTDINT
|
||||
#define LCARD_PSTDINT
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
|
||||
* do nothing else. On the Mac OS X version of gcc this is _STDINT_H_.
|
||||
*/
|
||||
|
||||
#if ((defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) \
|
||||
|| (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || (__WATCOMC__ >= 1250))) \
|
||||
|| (defined(__GNUC__)) \
|
||||
|| (defined (_MSC_VER) && (_MSC_VER >= 1600)) \
|
||||
|| (defined (__BORLANDC__) && (__BORLANDC__ >= 0x560))) && !defined (_PSTDINT_H_INCLUDED)
|
||||
#include <stdint.h>
|
||||
#define _PSTDINT_H_INCLUDED
|
||||
# ifndef PRINTF_INT64_MODIFIER
|
||||
# define PRINTF_INT64_MODIFIER "ll"
|
||||
# endif
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER "l"
|
||||
# endif
|
||||
# ifndef PRINTF_INT16_MODIFIER
|
||||
# define PRINTF_INT16_MODIFIER "h"
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_MODIFIER
|
||||
# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
|
||||
# endif
|
||||
# ifndef PRINTF_INT64_HEX_WIDTH
|
||||
# define PRINTF_INT64_HEX_WIDTH "16"
|
||||
# endif
|
||||
# ifndef PRINTF_INT32_HEX_WIDTH
|
||||
# define PRINTF_INT32_HEX_WIDTH "8"
|
||||
# endif
|
||||
# ifndef PRINTF_INT16_HEX_WIDTH
|
||||
# define PRINTF_INT16_HEX_WIDTH "4"
|
||||
# endif
|
||||
# ifndef PRINTF_INT8_HEX_WIDTH
|
||||
# define PRINTF_INT8_HEX_WIDTH "2"
|
||||
# endif
|
||||
# ifndef PRINTF_INT64_DEC_WIDTH
|
||||
# define PRINTF_INT64_DEC_WIDTH "20"
|
||||
# endif
|
||||
# ifndef PRINTF_INT32_DEC_WIDTH
|
||||
# define PRINTF_INT32_DEC_WIDTH "10"
|
||||
# endif
|
||||
# ifndef PRINTF_INT16_DEC_WIDTH
|
||||
# define PRINTF_INT16_DEC_WIDTH "5"
|
||||
# endif
|
||||
# ifndef PRINTF_INT8_DEC_WIDTH
|
||||
# define PRINTF_INT8_DEC_WIDTH "3"
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_HEX_WIDTH
|
||||
# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_DEC_WIDTH
|
||||
# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Something really weird is going on with Open Watcom. Just pull some of
|
||||
* these duplicated definitions from Open Watcom's stdint.h file for now.
|
||||
*/
|
||||
|
||||
# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
|
||||
# if !defined (INT64_C)
|
||||
# define INT64_C(x) (x + (INT64_MAX - INT64_MAX))
|
||||
# endif
|
||||
# if !defined (UINT64_C)
|
||||
# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
|
||||
# endif
|
||||
# if !defined (INT32_C)
|
||||
# define INT32_C(x) (x + (INT32_MAX - INT32_MAX))
|
||||
# endif
|
||||
# if !defined (UINT32_C)
|
||||
# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX))
|
||||
# endif
|
||||
# if !defined (INT16_C)
|
||||
# define INT16_C(x) (x)
|
||||
# endif
|
||||
# if !defined (UINT16_C)
|
||||
# define UINT16_C(x) (x)
|
||||
# endif
|
||||
# if !defined (INT8_C)
|
||||
# define INT8_C(x) (x)
|
||||
# endif
|
||||
# if !defined (UINT8_C)
|
||||
# define UINT8_C(x) (x)
|
||||
# endif
|
||||
# if !defined (UINT64_MAX)
|
||||
# define UINT64_MAX 18446744073709551615ULL
|
||||
# endif
|
||||
# if !defined (INT64_MAX)
|
||||
# define INT64_MAX 9223372036854775807LL
|
||||
# endif
|
||||
# if !defined (UINT32_MAX)
|
||||
# define UINT32_MAX 4294967295UL
|
||||
# endif
|
||||
# if !defined (INT32_MAX)
|
||||
# define INT32_MAX 2147483647L
|
||||
# endif
|
||||
# if !defined (INTMAX_MAX)
|
||||
# define INTMAX_MAX INT64_MAX
|
||||
# endif
|
||||
# if !defined (INTMAX_MIN)
|
||||
# define INTMAX_MIN INT64_MIN
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef _PSTDINT_H_INCLUDED
|
||||
#define _PSTDINT_H_INCLUDED
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX (~(size_t)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Deduce the type assignments from limits.h under the assumption that
|
||||
* integer sizes in bits are powers of 2, and follow the ANSI
|
||||
* definitions.
|
||||
*/
|
||||
|
||||
#ifndef UINT8_MAX
|
||||
# define UINT8_MAX 0xff
|
||||
#endif
|
||||
#ifndef uint8_t
|
||||
# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
|
||||
typedef unsigned char uint8_t;
|
||||
# define UINT8_C(v) ((uint8_t) v)
|
||||
# else
|
||||
# error "Platform not supported"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef INT8_MAX
|
||||
# define INT8_MAX 0x7f
|
||||
#endif
|
||||
#ifndef INT8_MIN
|
||||
# define INT8_MIN INT8_C(0x80)
|
||||
#endif
|
||||
#ifndef int8_t
|
||||
# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
|
||||
typedef signed char int8_t;
|
||||
# define INT8_C(v) ((int8_t) v)
|
||||
# else
|
||||
# error "Platform not supported"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef UINT16_MAX
|
||||
# define UINT16_MAX 0xffff
|
||||
#endif
|
||||
#ifndef uint16_t
|
||||
#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
|
||||
typedef unsigned int uint16_t;
|
||||
# ifndef PRINTF_INT16_MODIFIER
|
||||
# define PRINTF_INT16_MODIFIER ""
|
||||
# endif
|
||||
# define UINT16_C(v) ((uint16_t) (v))
|
||||
#elif (USHRT_MAX == UINT16_MAX)
|
||||
typedef unsigned short uint16_t;
|
||||
# define UINT16_C(v) ((uint16_t) (v))
|
||||
# ifndef PRINTF_INT16_MODIFIER
|
||||
# define PRINTF_INT16_MODIFIER "h"
|
||||
# endif
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INT16_MAX
|
||||
# define INT16_MAX 0x7fff
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
# define INT16_MIN INT16_C(0x8000)
|
||||
#endif
|
||||
#ifndef int16_t
|
||||
#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
|
||||
typedef signed int int16_t;
|
||||
# define INT16_C(v) ((int16_t) (v))
|
||||
# ifndef PRINTF_INT16_MODIFIER
|
||||
# define PRINTF_INT16_MODIFIER ""
|
||||
# endif
|
||||
#elif (SHRT_MAX == INT16_MAX)
|
||||
typedef signed short int16_t;
|
||||
# define INT16_C(v) ((int16_t) (v))
|
||||
# ifndef PRINTF_INT16_MODIFIER
|
||||
# define PRINTF_INT16_MODIFIER "h"
|
||||
# endif
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_MAX
|
||||
# define UINT32_MAX (0xffffffffUL)
|
||||
#endif
|
||||
#ifndef uint32_t
|
||||
#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
|
||||
typedef unsigned long uint32_t;
|
||||
# define UINT32_C(v) v ## UL
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER "l"
|
||||
# endif
|
||||
#elif (UINT_MAX == UINT32_MAX)
|
||||
typedef unsigned int uint32_t;
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER ""
|
||||
# endif
|
||||
# define UINT32_C(v) v ## U
|
||||
#elif (USHRT_MAX == UINT32_MAX)
|
||||
typedef unsigned short uint32_t;
|
||||
# define UINT32_C(v) ((unsigned short) (v))
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER ""
|
||||
# endif
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INT32_MAX
|
||||
# define INT32_MAX (0x7fffffffL)
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
# define INT32_MIN INT32_C(0x80000000)
|
||||
#endif
|
||||
#ifndef int32_t
|
||||
#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
|
||||
typedef signed long int32_t;
|
||||
# define INT32_C(v) v ## L
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER "l"
|
||||
# endif
|
||||
#elif (INT_MAX == INT32_MAX)
|
||||
typedef signed int int32_t;
|
||||
# define INT32_C(v) v
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER ""
|
||||
# endif
|
||||
#elif (SHRT_MAX == INT32_MAX)
|
||||
typedef signed short int32_t;
|
||||
# define INT32_C(v) ((short) (v))
|
||||
# ifndef PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_INT32_MODIFIER ""
|
||||
# endif
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The macro stdint_int64_defined is temporarily used to record
|
||||
* whether or not 64 integer support is available. It must be
|
||||
* defined for any 64 integer extensions for new platforms that are
|
||||
* added.
|
||||
*/
|
||||
|
||||
#undef stdint_int64_defined
|
||||
#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
|
||||
# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S)
|
||||
# define stdint_int64_defined
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
# define UINT64_C(v) v ## ULL
|
||||
# define INT64_C(v) v ## LL
|
||||
# ifndef PRINTF_INT64_MODIFIER
|
||||
# define PRINTF_INT64_MODIFIER "ll"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined (stdint_int64_defined)
|
||||
# if defined(__GNUC__)
|
||||
# define stdint_int64_defined
|
||||
__extension__ typedef long long int64_t;
|
||||
__extension__ typedef unsigned long long uint64_t;
|
||||
# define UINT64_C(v) v ## ULL
|
||||
# define INT64_C(v) v ## LL
|
||||
# ifndef PRINTF_INT64_MODIFIER
|
||||
# define PRINTF_INT64_MODIFIER "ll"
|
||||
# endif
|
||||
# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
|
||||
# define stdint_int64_defined
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
# define UINT64_C(v) v ## ULL
|
||||
# define INT64_C(v) v ## LL
|
||||
# ifndef PRINTF_INT64_MODIFIER
|
||||
# define PRINTF_INT64_MODIFIER "ll"
|
||||
# endif
|
||||
# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
|
||||
# define stdint_int64_defined
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
# define UINT64_C(v) v ## UI64
|
||||
# define INT64_C(v) v ## I64
|
||||
# ifndef PRINTF_INT64_MODIFIER
|
||||
# define PRINTF_INT64_MODIFIER "I64"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined (LONG_LONG_MAX) && defined (INT64_C)
|
||||
# define LONG_LONG_MAX INT64_C (9223372036854775807)
|
||||
#endif
|
||||
#ifndef ULONG_LONG_MAX
|
||||
# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
|
||||
#endif
|
||||
|
||||
#if !defined (INT64_MAX) && defined (INT64_C)
|
||||
# define INT64_MAX INT64_C (9223372036854775807)
|
||||
#endif
|
||||
#if !defined (INT64_MIN) && defined (INT64_C)
|
||||
# define INT64_MIN INT64_C (-9223372036854775808)
|
||||
#endif
|
||||
#if !defined (UINT64_MAX) && defined (INT64_C)
|
||||
# define UINT64_MAX UINT64_C (18446744073709551615)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Width of hexadecimal for number field.
|
||||
*/
|
||||
|
||||
#ifndef PRINTF_INT64_HEX_WIDTH
|
||||
# define PRINTF_INT64_HEX_WIDTH "16"
|
||||
#endif
|
||||
#ifndef PRINTF_INT32_HEX_WIDTH
|
||||
# define PRINTF_INT32_HEX_WIDTH "8"
|
||||
#endif
|
||||
#ifndef PRINTF_INT16_HEX_WIDTH
|
||||
# define PRINTF_INT16_HEX_WIDTH "4"
|
||||
#endif
|
||||
#ifndef PRINTF_INT8_HEX_WIDTH
|
||||
# define PRINTF_INT8_HEX_WIDTH "2"
|
||||
#endif
|
||||
|
||||
#ifndef PRINTF_INT64_DEC_WIDTH
|
||||
# define PRINTF_INT64_DEC_WIDTH "20"
|
||||
#endif
|
||||
#ifndef PRINTF_INT32_DEC_WIDTH
|
||||
# define PRINTF_INT32_DEC_WIDTH "10"
|
||||
#endif
|
||||
#ifndef PRINTF_INT16_DEC_WIDTH
|
||||
# define PRINTF_INT16_DEC_WIDTH "5"
|
||||
#endif
|
||||
#ifndef PRINTF_INT8_DEC_WIDTH
|
||||
# define PRINTF_INT8_DEC_WIDTH "3"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ok, lets not worry about 128 bit integers for now. Moore's law says
|
||||
* we don't need to worry about that until about 2040 at which point
|
||||
* we'll have bigger things to worry about.
|
||||
*/
|
||||
|
||||
#ifdef stdint_int64_defined
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
# define INTMAX_MAX INT64_MAX
|
||||
# define INTMAX_MIN INT64_MIN
|
||||
# define UINTMAX_MAX UINT64_MAX
|
||||
# define UINTMAX_C(v) UINT64_C(v)
|
||||
# define INTMAX_C(v) INT64_C(v)
|
||||
# ifndef PRINTF_INTMAX_MODIFIER
|
||||
# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_HEX_WIDTH
|
||||
# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_DEC_WIDTH
|
||||
# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
|
||||
# endif
|
||||
#else
|
||||
typedef int32_t intmax_t;
|
||||
typedef uint32_t uintmax_t;
|
||||
# define INTMAX_MAX INT32_MAX
|
||||
# define UINTMAX_MAX UINT32_MAX
|
||||
# define UINTMAX_C(v) UINT32_C(v)
|
||||
# define INTMAX_C(v) INT32_C(v)
|
||||
# ifndef PRINTF_INTMAX_MODIFIER
|
||||
# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_HEX_WIDTH
|
||||
# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
|
||||
# endif
|
||||
# ifndef PRINTF_INTMAX_DEC_WIDTH
|
||||
# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Because this file currently only supports platforms which have
|
||||
* precise powers of 2 as bit sizes for the default integers, the
|
||||
* least definitions are all trivial. Its possible that a future
|
||||
* version of this file could have different definitions.
|
||||
*/
|
||||
|
||||
#ifndef stdint_least_defined
|
||||
typedef int8_t int_least8_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
|
||||
# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
|
||||
# define UINT_LEAST8_MAX UINT8_MAX
|
||||
# define INT_LEAST8_MAX INT8_MAX
|
||||
# define UINT_LEAST16_MAX UINT16_MAX
|
||||
# define INT_LEAST16_MAX INT16_MAX
|
||||
# define UINT_LEAST32_MAX UINT32_MAX
|
||||
# define INT_LEAST32_MAX INT32_MAX
|
||||
# define INT_LEAST8_MIN INT8_MIN
|
||||
# define INT_LEAST16_MIN INT16_MIN
|
||||
# define INT_LEAST32_MIN INT32_MIN
|
||||
# ifdef stdint_int64_defined
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
|
||||
# define UINT_LEAST64_MAX UINT64_MAX
|
||||
# define INT_LEAST64_MAX INT64_MAX
|
||||
# define INT_LEAST64_MIN INT64_MIN
|
||||
# endif
|
||||
#endif
|
||||
#undef stdint_least_defined
|
||||
|
||||
/*
|
||||
* The ANSI C committee pretending to know or specify anything about
|
||||
* performance is the epitome of misguided arrogance. The mandate of
|
||||
* this file is to *ONLY* ever support that absolute minimum
|
||||
* definition of the fast integer types, for compatibility purposes.
|
||||
* No extensions, and no attempt to suggest what may or may not be a
|
||||
* faster integer type will ever be made in this file. Developers are
|
||||
* warned to stay away from these types when using this or any other
|
||||
* stdint.h.
|
||||
*/
|
||||
|
||||
typedef int_least8_t int_fast8_t;
|
||||
typedef uint_least8_t uint_fast8_t;
|
||||
typedef int_least16_t int_fast16_t;
|
||||
typedef uint_least16_t uint_fast16_t;
|
||||
typedef int_least32_t int_fast32_t;
|
||||
typedef uint_least32_t uint_fast32_t;
|
||||
#define UINT_FAST8_MAX UINT_LEAST8_MAX
|
||||
#define INT_FAST8_MAX INT_LEAST8_MAX
|
||||
#define UINT_FAST16_MAX UINT_LEAST16_MAX
|
||||
#define INT_FAST16_MAX INT_LEAST16_MAX
|
||||
#define UINT_FAST32_MAX UINT_LEAST32_MAX
|
||||
#define INT_FAST32_MAX INT_LEAST32_MAX
|
||||
#define INT_FAST8_MIN INT_LEAST8_MIN
|
||||
#define INT_FAST16_MIN INT_LEAST16_MIN
|
||||
#define INT_FAST32_MIN INT_LEAST32_MIN
|
||||
#ifdef stdint_int64_defined
|
||||
typedef int_least64_t int_fast64_t;
|
||||
typedef uint_least64_t uint_fast64_t;
|
||||
# define UINT_FAST64_MAX UINT_LEAST64_MAX
|
||||
# define INT_FAST64_MAX INT_LEAST64_MAX
|
||||
# define INT_FAST64_MIN INT_LEAST64_MIN
|
||||
#endif
|
||||
|
||||
#undef stdint_int64_defined
|
||||
|
||||
/*
|
||||
* Whatever piecemeal, per compiler thing we can do about the wchar_t
|
||||
* type limits.
|
||||
*/
|
||||
|
||||
#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__)
|
||||
# include <wchar.h>
|
||||
# ifndef WCHAR_MIN
|
||||
# define WCHAR_MIN 0
|
||||
# endif
|
||||
# ifndef WCHAR_MAX
|
||||
# define WCHAR_MAX ((wchar_t)-1)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Whatever piecemeal, per compiler/platform thing we can do about the
|
||||
* (u)intptr_t types and limits.
|
||||
*/
|
||||
|
||||
#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)
|
||||
# define STDINT_H_UINTPTR_T_DEFINED
|
||||
#elif defined (_CVI_)
|
||||
# define STDINT_H_UINTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef STDINT_H_UINTPTR_T_DEFINED
|
||||
# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64)
|
||||
# define stdint_intptr_bits 64
|
||||
# elif defined (__WATCOMC__) || defined (__TURBOC__)
|
||||
# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
|
||||
# define stdint_intptr_bits 16
|
||||
# else
|
||||
# define stdint_intptr_bits 32
|
||||
# endif
|
||||
# elif defined (__i386__) || defined (_WIN32) || defined (WIN32)
|
||||
# define stdint_intptr_bits 32
|
||||
# elif defined (__INTEL_COMPILER)
|
||||
/* TODO -- what did Intel do about x86-64? */
|
||||
# endif
|
||||
|
||||
# ifdef stdint_intptr_bits
|
||||
# define stdint_intptr_glue3_i(a,b,c) a##b##c
|
||||
# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c)
|
||||
# ifndef PRINTF_INTPTR_MODIFIER
|
||||
# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
|
||||
# endif
|
||||
# ifndef PTRDIFF_MAX
|
||||
# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
|
||||
# endif
|
||||
# ifndef PTRDIFF_MIN
|
||||
# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
|
||||
# endif
|
||||
# ifndef UINTPTR_MAX
|
||||
# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
|
||||
# endif
|
||||
# ifndef INTPTR_MAX
|
||||
# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
|
||||
# endif
|
||||
# ifndef INTPTR_MIN
|
||||
# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
|
||||
# endif
|
||||
# ifndef INTPTR_C
|
||||
# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
|
||||
# endif
|
||||
# ifndef UINTPTR_C
|
||||
# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
|
||||
# endif
|
||||
typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
|
||||
typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t;
|
||||
# else
|
||||
/* TODO -- This following is likely wrong for some platforms, and does
|
||||
nothing for the definition of uintptr_t. */
|
||||
typedef ptrdiff_t intptr_t;
|
||||
# endif
|
||||
# define STDINT_H_UINTPTR_T_DEFINED
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assumes sig_atomic_t is signed and we have a 2s complement machine.
|
||||
*/
|
||||
|
||||
#ifndef SIG_ATOMIC_MAX
|
||||
# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (__TEST_PSTDINT_FOR_CORRECTNESS)
|
||||
|
||||
/*
|
||||
* Please compile with the maximum warning settings to make sure macros are not
|
||||
* defined more than once.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define glue3_aux(x,y,z) x ## y ## z
|
||||
#define glue3(x,y,z) glue3_aux(x,y,z)
|
||||
|
||||
#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0);
|
||||
#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0);
|
||||
|
||||
#define DECL(us,bits) glue3(DECL,us,) (bits)
|
||||
|
||||
#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits)
|
||||
|
||||
int main () {
|
||||
DECL(I,8)
|
||||
DECL(U,8)
|
||||
DECL(I,16)
|
||||
DECL(U,16)
|
||||
DECL(I,32)
|
||||
DECL(U,32)
|
||||
#ifdef INT64_MAX
|
||||
DECL(I,64)
|
||||
DECL(U,64)
|
||||
#endif
|
||||
intmax_t imax = INTMAX_C(0);
|
||||
uintmax_t umax = UINTMAX_C(0);
|
||||
char str0[256], str1[256];
|
||||
|
||||
sprintf (str0, "%d %x\n", 0, ~0);
|
||||
|
||||
sprintf (str1, "%d %x\n", i8, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1);
|
||||
sprintf (str1, "%u %x\n", u8, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1);
|
||||
sprintf (str1, "%d %x\n", i16, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1);
|
||||
sprintf (str1, "%u %x\n", u16, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1);
|
||||
sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1);
|
||||
sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1);
|
||||
#ifdef INT64_MAX
|
||||
sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1);
|
||||
#endif
|
||||
sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1);
|
||||
sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0);
|
||||
if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1);
|
||||
|
||||
TESTUMAX(8);
|
||||
TESTUMAX(16);
|
||||
TESTUMAX(32);
|
||||
#ifdef INT64_MAX
|
||||
TESTUMAX(64);
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user