shithub: rgbds

ref: 02fe73d1f38b702b7a94a8b4934eb9e829eb4278
dir: /src/asm/charmap.c/

View raw version
/*
 * This file is part of RGBDS.
 *
 * Copyright (c) 2013-2018, stag019 and RGBDS contributors.
 *
 * SPDX-License-Identifier: MIT
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "asm/asm.h"
#include "asm/charmap.h"
#include "asm/main.h"
#include "asm/output.h"
#include "asm/util.h"
#include "asm/warning.h"

#define CHARMAP_HASH_SIZE (1 << 9)

struct CharmapStackEntry {
	struct Charmap *charmap;
	struct CharmapStackEntry *next;
};

static struct Charmap *tHashedCharmaps[CHARMAP_HASH_SIZE];

static struct Charmap *mainCharmap;
static struct Charmap *currentCharmap;

struct CharmapStackEntry *charmapStack;

static uint32_t charmap_CalcHash(const char *s)
{
	return calchash(s) % CHARMAP_HASH_SIZE;
}

static struct Charmap **charmap_Get(const char *name)
{
	struct Charmap **ppCharmap = &tHashedCharmaps[charmap_CalcHash(name)];

	while (*ppCharmap != NULL && strcmp((*ppCharmap)->name, name))
		ppCharmap = &(*ppCharmap)->next;

	return ppCharmap;
}

static void CopyNode(struct Charmap *dest,
		     const struct Charmap *src,
		     int nodeIdx)
{
	dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
	dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
	for (int i = 0; i < 256; i++)
		if (src->nodes[nodeIdx].next[i])
			dest->nodes[nodeIdx].next[i] = dest->nodes +
				(src->nodes[nodeIdx].next[i] - src->nodes);
}

struct Charmap *charmap_New(const char *name, const char *baseName)
{
	struct Charmap *pBase = NULL;

	if (baseName != NULL) {
		struct Charmap **ppBase = charmap_Get(baseName);

		if (*ppBase == NULL) {
			yyerror("Base charmap '%s' doesn't exist", baseName);
			return NULL;
		}

		pBase = *ppBase;
	}

	struct Charmap **ppCharmap = charmap_Get(name);

	if (*ppCharmap != NULL) {
		yyerror("Charmap '%s' already exists", name);
		return NULL;
	}

	*ppCharmap = calloc(1, sizeof(struct Charmap));

	if (*ppCharmap == NULL)
		fatalerror("Not enough memory for charmap");

	struct Charmap *pCharmap = *ppCharmap;

	snprintf(pCharmap->name, sizeof(pCharmap->name), "%s", name);

	if (pBase != NULL) {
		pCharmap->charCount = pBase->charCount;
		pCharmap->nodeCount = pBase->nodeCount;

		for (int i = 0; i < MAXCHARNODES; i++)
			CopyNode(pCharmap, pBase, i);
	}

	currentCharmap = pCharmap;

	return pCharmap;
}

void charmap_Set(const char *name)
{
	struct Charmap **ppCharmap = charmap_Get(name);

	if (*ppCharmap == NULL) {
		yyerror("Charmap '%s' doesn't exist", name);
		return;
	}

	currentCharmap = *ppCharmap;
}

void charmap_Push(void)
{
	struct CharmapStackEntry *stackEntry;

	stackEntry = malloc(sizeof(struct CharmapStackEntry));
	if (stackEntry == NULL)
		fatalerror("No memory for charmap stack");

	stackEntry->charmap = currentCharmap;
	stackEntry->next = charmapStack;

	charmapStack = stackEntry;
}

void charmap_Pop(void)
{
	if (charmapStack == NULL)
		fatalerror("No entries in the charmap stack");

	struct CharmapStackEntry *top = charmapStack;

	currentCharmap = top->charmap;
	charmapStack = top->next;
	free(top);
}

void charmap_InitMain(void)
{
	mainCharmap = charmap_New("main", NULL);
}

int32_t charmap_Add(char *input, uint8_t output)
{
	int32_t i;
	uint8_t v;

	struct Charmap  *charmap = currentCharmap;
	struct Charnode *curr_node, *temp_node;

	if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
		return -1;

	curr_node = &charmap->nodes[0];

	for (i = 0; (v = (uint8_t)input[i]); i++) {
		if (curr_node->next[v]) {
			curr_node = curr_node->next[v];
		} else {
			temp_node = &charmap->nodes[charmap->nodeCount + 1];

			curr_node->next[v] = temp_node;
			curr_node = temp_node;

			++charmap->nodeCount;
		}
	}

	/* prevent duplicated keys by accepting only first key-value pair.  */
	if (curr_node->isCode)
		return charmap->charCount;

	curr_node->code = output;
	curr_node->isCode = 1;

	return ++charmap->charCount;
}

int32_t charmap_Convert(char **input)
{
	struct Charmap  *charmap = currentCharmap;
	struct Charnode *charnode;

	char *output;
	char outchar[8];

	int32_t i, match, length;
	uint8_t v, foundCode;

	output = malloc(strlen(*input));
	if (output == NULL)
		fatalerror("Not enough memory for buffer");

	length = 0;

	while (**input) {
		charnode = &charmap->nodes[0];

		/*
		 * Find the longest valid match which has been registered in
		 * charmap, possibly yielding multiple or no matches.
		 * The longest match is taken, meaning partial matches shorter
		 * than the longest one are ignored.
		 */
		for (i = match = 0; (v = (*input)[i]);) {
			if (!charnode->next[v])
				break;

			charnode = charnode->next[v];
			i++;

			if (charnode->isCode) {
				match = i;
				foundCode = charnode->code;
			}
		}

		if (match) {
			output[length] = foundCode;

			length += 1;
		} else {
			/*
			 * put a utf-8 character
			 * if failed to find a match.
			 */
			match = readUTF8Char(outchar, *input);

			if (match) {
				memcpy(output + length, *input, match);
			} else {
				output[length] = 0;
				match = 1;
			}

			length += match;
		}

		*input += match;
	}

	*input = output;

	return length;
}