forked from EXTERNAL/bareiron
207 lines
5.7 KiB
C
207 lines
5.7 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef ESP_PLATFORM
|
|
#include "lwip/sockets.h"
|
|
#include "lwip/netdb.h"
|
|
#include "esp_timer.h"
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#ifndef CLOCK_MONOTONIC
|
|
#define CLOCK_MONOTONIC 1
|
|
#endif
|
|
#endif
|
|
|
|
#include "globals.h"
|
|
#include "varnum.h"
|
|
#include "tools.h"
|
|
|
|
static uint64_t htonll (uint64_t value) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return ((uint64_t)htonl((uint32_t)(value >> 32))) |
|
|
((uint64_t)htonl((uint32_t)(value & 0xFFFFFFFF)) << 32);
|
|
#else
|
|
return value;
|
|
#endif
|
|
}
|
|
|
|
// Keep track of the total amount of bytes received with recv_all
|
|
// Helps notice misread packets and clean up after errors
|
|
uint64_t total_bytes_received = 0;
|
|
|
|
ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) {
|
|
char *p = buf;
|
|
size_t total = 0;
|
|
|
|
// If requested, exit early when first byte not immediately available
|
|
if (require_first) {
|
|
ssize_t r = recv(client_fd, p, 1, MSG_PEEK);
|
|
if (r <= 0) {
|
|
if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
|
return 0; // no first byte available yet
|
|
}
|
|
return -1; // error or connection closed
|
|
}
|
|
}
|
|
|
|
// Busy-wait (with task yielding) until we get exactly n bytes
|
|
while (total < n) {
|
|
ssize_t r = recv(client_fd, p + total, n - total, 0);
|
|
if (r < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
task_yield();
|
|
continue;
|
|
} else {
|
|
total_bytes_received += total;
|
|
return -1; // real error
|
|
}
|
|
} else if (r == 0) {
|
|
// connection closed before full read
|
|
total_bytes_received += total;
|
|
return total;
|
|
}
|
|
total += r;
|
|
}
|
|
|
|
total_bytes_received += total;
|
|
return total; // got exactly n bytes
|
|
}
|
|
|
|
ssize_t send_all (int client_fd, const void *buf, ssize_t len) {
|
|
// Treat any input buffer as *uint8_t for simplicity
|
|
const uint8_t *p = (const uint8_t *)buf;
|
|
ssize_t sent = 0;
|
|
|
|
// Busy-wait (with task yielding) until all data has been sent
|
|
while (sent < len) {
|
|
ssize_t n = send(client_fd, p + sent, len - sent, MSG_NOSIGNAL);
|
|
if (n > 0) { // some data was sent, log it
|
|
sent += n;
|
|
continue;
|
|
}
|
|
if (n == 0) { // connection was closed, treat this as an error
|
|
errno = ECONNRESET;
|
|
return -1;
|
|
}
|
|
// not yet ready to transmit, try again
|
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
task_yield();
|
|
continue;
|
|
}
|
|
return -1; // real error
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t writeByte (int client_fd, uint8_t byte) {
|
|
return send_all(client_fd, &byte, 1);
|
|
}
|
|
ssize_t writeUint16 (int client_fd, uint16_t num) {
|
|
uint16_t be = htons(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeUint32 (int client_fd, uint32_t num) {
|
|
uint32_t be = htonl(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeUint64 (int client_fd, uint64_t num) {
|
|
uint64_t be = htonll(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeFloat (int client_fd, float num) {
|
|
uint32_t bits;
|
|
memcpy(&bits, &num, sizeof(bits));
|
|
bits = htonl(bits);
|
|
return send_all(client_fd, &bits, sizeof(bits));
|
|
}
|
|
ssize_t writeDouble (int client_fd, double num) {
|
|
uint64_t bits;
|
|
memcpy(&bits, &num, sizeof(bits));
|
|
bits = htonll(bits);
|
|
return send_all(client_fd, &bits, sizeof(bits));
|
|
}
|
|
|
|
uint8_t readByte (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 1, false);
|
|
return recv_buffer[0];
|
|
}
|
|
uint16_t readUint16 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 2, false);
|
|
return ((uint16_t)recv_buffer[0] << 8) | recv_buffer[1];
|
|
}
|
|
uint32_t readUint32 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 4, false);
|
|
return ((uint32_t)recv_buffer[0] << 24) |
|
|
((uint32_t)recv_buffer[1] << 16) |
|
|
((uint32_t)recv_buffer[2] << 8) |
|
|
((uint32_t)recv_buffer[3]);
|
|
}
|
|
uint64_t readUint64 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
|
return ((uint64_t)recv_buffer[0] << 56) |
|
|
((uint64_t)recv_buffer[1] << 48) |
|
|
((uint64_t)recv_buffer[2] << 40) |
|
|
((uint64_t)recv_buffer[3] << 32) |
|
|
((uint64_t)recv_buffer[4] << 24) |
|
|
((uint64_t)recv_buffer[5] << 16) |
|
|
((uint64_t)recv_buffer[6] << 8) |
|
|
((uint64_t)recv_buffer[7]);
|
|
}
|
|
int64_t readInt64 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
|
return ((int64_t)recv_buffer[0] << 56) |
|
|
((int64_t)recv_buffer[1] << 48) |
|
|
((int64_t)recv_buffer[2] << 40) |
|
|
((int64_t)recv_buffer[3] << 32) |
|
|
((int64_t)recv_buffer[4] << 24) |
|
|
((int64_t)recv_buffer[5] << 16) |
|
|
((int64_t)recv_buffer[6] << 8) |
|
|
((int64_t)recv_buffer[7]);
|
|
}
|
|
float readFloat (int client_fd) {
|
|
uint32_t bytes = readUint32(client_fd);
|
|
float output;
|
|
memcpy(&output, &bytes, sizeof(output));
|
|
return output;
|
|
}
|
|
double readDouble (int client_fd) {
|
|
uint64_t bytes = readUint64(client_fd);
|
|
double output;
|
|
memcpy(&output, &bytes, sizeof(output));
|
|
return output;
|
|
}
|
|
|
|
// Reads a networked string into recv_buffer
|
|
void readString (int client_fd) {
|
|
uint32_t length = readVarInt(client_fd);
|
|
recv_count = recv_all(client_fd, recv_buffer, length, false);
|
|
recv_buffer[recv_count] = '\0';
|
|
}
|
|
|
|
uint32_t fast_rand () {
|
|
rng_seed ^= rng_seed << 13;
|
|
rng_seed ^= rng_seed >> 17;
|
|
rng_seed ^= rng_seed << 5;
|
|
return rng_seed;
|
|
}
|
|
|
|
uint64_t splitmix64 (uint64_t state) {
|
|
uint64_t z = state + 0x9e3779b97f4a7c15;
|
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
|
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
|
return z ^ (z >> 31);
|
|
}
|
|
|
|
#ifndef ESP_PLATFORM
|
|
int64_t get_program_time () {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (int64_t)ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL;
|
|
}
|
|
#endif
|