Rename kamil_adc to rph_radar and fix its data path

This commit is contained in:
Ayzen
2026-10-06 18:15:18 +03:00
parent aef549ea76
commit a185e61495
94 changed files with 2851 additions and 9485 deletions
@@ -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