<commit_before>/*
 * libdivecomputer
 *
 * Copyright (C) 2013 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 */

#include <string.h> // memcmp, memcpy
#include <stdlib.h> // malloc, free

#include "shearwater_common.h"

#include "context-private.h"
#include "array.h"

#define SZ_PACKET  254

// SLIP special character codes
#define END       0xC0
#define ESC       0xDB
#define ESC_END   0xDC
#define ESC_ESC   0xDD

#define EXITCODE(n) ((n) < 0 ? (n) : 0)

dc_status_t
shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name)
{
	// Open the device.
	int rc = serial_open (&device->port, context, name);
	if (rc == -1) {
		ERROR (context, "Failed to open the serial port.");
		return DC_STATUS_IO;
	}

	// Set the serial communication protocol (115200 8N1).
	rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
	if (rc == -1) {
		ERROR (context, "Failed to set the terminal attributes.");
		serial_close (device->port);
		return DC_STATUS_IO;
	}

	// Set the timeout for receiving data (3000ms).
	if (serial_set_timeout (device->port, 3000) == -1) {
		ERROR (context, "Failed to set the timeout.");
		serial_close (device->port);
		return DC_STATUS_IO;
	}

	// Make sure everything is in a sane state.
	serial_sleep (device->port, 300);
	serial_flush (device->port, SERIAL_QUEUE_BOTH);

	return DC_STATUS_SUCCESS;
}


dc_status_t
shearwater_common_close (shearwater_common_device_t *device)
{
	// Close the device.
	if (serial_close (device->port) == -1) {
		return DC_STATUS_IO;
	}

	return DC_STATUS_SUCCESS;
}


static int
shearwater_common_decompress_lre (unsigned char *data, unsigned int size, dc_buffer_t *buffer, unsigned int *isfinal)
{
	// The RLE decompression algorithm does interpret the binary data as a
	// stream of 9 bit values. Therefore, the total number of bits needs to be
	// a multiple of 9 bits.
	unsigned int nbits = size * 8;
	if (nbits % 9 != 0)
		return -1;

	unsigned int offset = 0;
	while (offset + 9 <= nbits) {
		// Extract the 9 bit value.
		unsigned int byte = offset / 8;
		unsigned int bit  = offset % 8;
		unsigned int shift = 16 - (bit + 9);
		unsigned int value = (array_uint16_be (data + byte) >> shift) & 0x1FF;

		// The 9th bit indicates whether the remaining 8 bits represent
		// a run of zero bytes or not. If the bit is set, the value is
		// not a run and doesn’t need expansion. If the bit is not set,
		// the value contains the number of zero bytes in the run. A
		// zero-length run indicates the end of the compressed stream.
		if (value & 0x100) {
			// Append the data byte directly.
			unsigned char c = value & 0xFF;
			if (!dc_buffer_append (buffer, &c, 1))
				return -1;
		} else if (value == 0) {
			// Reached the end of the compressed stream.
			if (isfinal)
				*isfinal = 1;
			break;
		} else {
			// Expand the run with zero bytes.
			if (!dc_buffer_resize (buffer, dc_buffer_get_size (buffer) + value))
				return -1;
		}

		offset += 9;
	}

	return 0;
}


static int
shearwater_common_decompress_xor (unsigned char *data, unsigned int size)
{
	// Each block of 32 bytes is XOR'ed (in-place) with the previous block,
	// except for the first block, which is passed through unchanged.
	for (unsigned int i = 32; i < size; ++i) {
		data[i] ^= data[i - 32];
	}

	return 0;
}


static int
shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned char data[], unsigned int size)
{
	int n = 0;
	const unsigned char end[] = {END};
	const unsigned char esc_end[] = {ESC, ESC_END};
	const unsigned char esc_esc[] = {ESC, ESC_ESC};

#if 0
	// Send an initial END character to flush out any data that may have
	// accumulated in the receiver due to line noise.
	n = serial_write (device->port, end, sizeof (end));
	if (n != sizeof (end)) {
		return EXITCODE(n);
	}
#endif

	for (unsigned int i = 0; i < size; ++i) {
		const unsigned char *seq = NULL;
		unsigned int len = 0;
		switch (data[i]) {
		case END:
			// Escape the END character.
			seq = esc_end;
			len = sizeof (esc_end);
			break;
		case ESC:
			// Escape the ESC character.
			seq = esc_esc;
			len = sizeof (esc_esc);
			break;
		default:
			// Normal character.
			seq = data + i;
			len = 1;
			break;
		}

		n = serial_write (device->port, seq, len);
		if (n != len) {
			return EXITCODE(n);
		}
	}

	// Send the END character to indicate the end of the packet.
	n = serial_write (device->port, end, sizeof (end));
	if (n != sizeof (end)) {
		return EXITCODE(n);
	}

	return size;
}


static int<commit_msg>Buffer the slip packet before sending.<commit_after>/*
 * libdivecomputer
 *
 * Copyright (C) 2013 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 */

#include <string.h> // memcmp, memcpy
#include <stdlib.h> // malloc, free

#include "shearwater_common.h"

#include "context-private.h"
#include "array.h"

#define SZ_PACKET  254

// SLIP special character codes
#define END       0xC0
#define ESC       0xDB
#define ESC_END   0xDC
#define ESC_ESC   0xDD

#define EXITCODE(n) ((n) < 0 ? (n) : 0)

dc_status_t
shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name)
{
	// Open the device.
	int rc = serial_open (&device->port, context, name);
	if (rc == -1) {
		ERROR (context, "Failed to open the serial port.");
		return DC_STATUS_IO;
	}

	// Set the serial communication protocol (115200 8N1).
	rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
	if (rc == -1) {
		ERROR (context, "Failed to set the terminal attributes.");
		serial_close (device->port);
		return DC_STATUS_IO;
	}

	// Set the timeout for receiving data (3000ms).
	if (serial_set_timeout (device->port, 3000) == -1) {
		ERROR (context, "Failed to set the timeout.");
		serial_close (device->port);
		return DC_STATUS_IO;
	}

	// Make sure everything is in a sane state.
	serial_sleep (device->port, 300);
	serial_flush (device->port, SERIAL_QUEUE_BOTH);

	return DC_STATUS_SUCCESS;
}


dc_status_t
shearwater_common_close (shearwater_common_device_t *device)
{
	// Close the device.
	if (serial_close (device->port) == -1) {
		return DC_STATUS_IO;
	}

	return DC_STATUS_SUCCESS;
}


static int
shearwater_common_decompress_lre (unsigned char *data, unsigned int size, dc_buffer_t *buffer, unsigned int *isfinal)
{
	// The RLE decompression algorithm does interpret the binary data as a
	// stream of 9 bit values. Therefore, the total number of bits needs to be
	// a multiple of 9 bits.
	unsigned int nbits = size * 8;
	if (nbits % 9 != 0)
		return -1;

	unsigned int offset = 0;
	while (offset + 9 <= nbits) {
		// Extract the 9 bit value.
		unsigned int byte = offset / 8;
		unsigned int bit  = offset % 8;
		unsigned int shift = 16 - (bit + 9);
		unsigned int value = (array_uint16_be (data + byte) >> shift) & 0x1FF;

		// The 9th bit indicates whether the remaining 8 bits represent
		// a run of zero bytes or not. If the bit is set, the value is
		// not a run and doesn’t need expansion. If the bit is not set,
		// the value contains the number of zero bytes in the run. A
		// zero-length run indicates the end of the compressed stream.
		if (value & 0x100) {
			// Append the data byte directly.
			unsigned char c = value & 0xFF;
			if (!dc_buffer_append (buffer, &c, 1))
				return -1;
		} else if (value == 0) {
			// Reached the end of the compressed stream.
			if (isfinal)
				*isfinal = 1;
			break;
		} else {
			// Expand the run with zero bytes.
			if (!dc_buffer_resize (buffer, dc_buffer_get_size (buffer) + value))
				return -1;
		}

		offset += 9;
	}

	return 0;
}


static int
shearwater_common_decompress_xor (unsigned char *data, unsigned int size)
{
	// Each block of 32 bytes is XOR'ed (in-place) with the previous block,
	// except for the first block, which is passed through unchanged.
	for (unsigned int i = 32; i < size; ++i) {
		data[i] ^= data[i - 32];
	}

	return 0;
}


static int
shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned char data[], unsigned int size)
{
	int n = 0;
	const unsigned char end[] = {END};
	const unsigned char esc_end[] = {ESC, ESC_END};
	const unsigned char esc_esc[] = {ESC, ESC_ESC};
	unsigned char buffer[32];
	unsigned int nbytes = 0;

#if 0
	// Send an initial END character to flush out any data that may have
	// accumulated in the receiver due to line noise.
	n = serial_write (device->port, end, sizeof (end));
	if (n != sizeof (end)) {
		return EXITCODE(n);
	}
#endif

	for (unsigned int i = 0; i < size; ++i) {
		const unsigned char *seq = NULL;
		unsigned int len = 0;
		switch (data[i]) {
		case END:
			// Escape the END character.
			seq = esc_end;
			len = sizeof (esc_end);
			break;
		case ESC:
			// Escape the ESC character.
			seq = esc_esc;
			len = sizeof (esc_esc);
			break;
		default:
			// Normal character.
			seq = data + i;
			len = 1;
			break;
		}

		// Flush the buffer if necessary.
		if (nbytes + len + sizeof(end) > sizeof(buffer)) {
			n = serial_write (device->port, buffer, nbytes);
			if (n != nbytes) {
				return EXITCODE(n);
			}

			nbytes = 0;
		}

		// Append the escaped character.
		memcpy(buffer + nbytes, seq, len);
		nbytes += len;
	}

	// Append the END character to indicate the end of the packet.
	memcpy(buffer + nbytes, end, sizeof(end));
	nbytes += sizeof(end);

	// Flush the buffer.
	n = serial_write (device->port, buffer, nbytes);
	if (n != nbytes) {
		return EXITCODE(n);
	}

	return size;
}


static int
{'fraction_non_alphanumeric': 0.10751700055136923, 'fraction_numerical': 0.01911413343135453, 'mean_word_length': 3.3169377231257435, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6504405', 'n_tokens_mistral': 3988, 'n_tokens_neox': 3569, 'n_words': 1406}