/** @file
 * @brief Communication with worker processes
 */
/* Copyright (C) 2011,2022,2023 Olly Betts
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see
 * <https://www.gnu.org/licenses/>.
 */

#include <config.h>

#include "worker_comms.h"

using namespace std;

const int buffer_size = 4096;

bool
read_string(FILE* f, string& s)
{
    // Extracting the length of the string
    int ch = GETC(f);
    if (ch < 0) return false;
    size_t len = ch;
    if (len >= 253) {
	unsigned i = len - 251;
	len = 0;
	for ( ; i; --i) {
	    ch = GETC(f);
	    if (ch < 0) return false;
	    len = (len << 8) | ch;
	}
    }

    s.resize(0);
    s.reserve(len);

    // Reading the string
    char buf[buffer_size];

    while (len) {
	size_t n = FREAD(buf, 1, min(sizeof(buf), len), f);
	if (n == 0) {
	    // Error or EOF!
	    return false;
	}
	s.append(buf, size_t(n));
	len -= n;
    }

    return true;
}

bool
write_string(FILE* f, const char* p, size_t len)
{
    if (len < 253) {
	PUTC(static_cast<unsigned char>(len), f);
    } else if (len < 0x10000) {
	PUTC(253, f);
	PUTC(static_cast<unsigned char>(len >> 8), f);
	PUTC(static_cast<unsigned char>(len), f);
    } else if (len < 0x1000000) {
	PUTC(254, f);
	PUTC(static_cast<unsigned char>(len >> 16), f);
	PUTC(static_cast<unsigned char>(len >> 8), f);
	PUTC(static_cast<unsigned char>(len), f);
    } else {
	PUTC(255, f);
	PUTC(static_cast<unsigned char>(len >> 24), f);
	PUTC(static_cast<unsigned char>(len >> 16), f);
	PUTC(static_cast<unsigned char>(len >> 8), f);
	PUTC(static_cast<unsigned char>(len), f);
    }

    while (len) {
	size_t n = FWRITE(p, 1, len, f);
	if (n == 0) {
	    // EOF.
	    return false;
	}
	p += n;
	len -= n;
    }

    return true;
}

bool
read_unsigned(FILE* f, unsigned& v)
{
    unsigned char data[(sizeof(v) * 8 + 6) / 7];
    unsigned char* p = data;
    int ch;
    do {
	ch = GETC(f);
	if (p - data == sizeof(data) || ch < 0) return false;
	*p++ = ch & 0x7f;
    } while (ch & 0x80);
    v = 0;
    do {
	--p;
	v = (v << 7) | *p;
    } while (p > data);
    return true;
}

bool
read_unsigned(FILE* f, unsigned long& v)
{
    unsigned char data[(sizeof(v) * 8 + 6) / 7];
    unsigned char* p = data;
    int ch;
    do {
	ch = GETC(f);
	if (p - data == sizeof(data) || ch < 0) return false;
	*p++ = ch & 0x7f;
    } while (ch & 0x80);
    v = 0;
    do {
	--p;
	v = (v << 7) | *p;
    } while (p > data);
    return true;
}

bool
write_unsigned(FILE* f, unsigned long v)
{
    while (v >= 0x80) {
	if (PUTC(v | 0x80, f) < 0) return false;
	v >>= 7;
    }
    return !(PUTC(v, f) < 0);
}

bool
write_unsigned(FILE* f, unsigned v)
{
    while (v >= 0x80) {
	if (PUTC(v | 0x80, f) < 0) return false;
	v >>= 7;
    }
    return !(PUTC(v, f) < 0);
}
