/* * singly_linked_queue.h * * A abstract data type for queues of strings. */ /* * The type Queue will be some kind of pointer. That is all a client * of this "module" is allowed to know. All the internal details * of the queue will be buried inside another file, invisible to the * client. I think this is about the best we can do in C. */ typedef struct Q* Queue; /* Returns a newly allocated, empty queue, or NULL if the queue could not be created. */ Queue create(); /* Returns the number of items in the queue. */ int size(Queue queue); /* Adds a copy of the given string to the queue. */ void enqueue(Queue queue, char* item); /* Removes the first item from the queue and returns the string that was stored there. Returns NULL if the queue was empty. The memory for this item must be freed by the caller. */ char* dequeue(Queue queue); /* Returns whether the queue contains the given string (using strcmp) */ int contains(Queue queue, char* item); /* Frees up all the memory taken up by the queue's contents and the queue itself. */ void destroy(Queue queue);
/*
* singly_linked_queue.c
*
* Implementation of the abstract data type for queues-of-strings.
*/
#include <stdlib.h>
#include <string.h>
#include "singly_linked_queue.h"
/*
* We'll do the singly-linked list representation, with head and
* tail pointers. We'll also keep track of the size, to make
* querying the size really fast.
*/
struct Node {
char* item;
struct Node* next;
};
struct Q {
struct Node* head;
struct Node* tail;
unsigned int size;
};
/*
* Creates and returns a new empty queue; returns NULL if the
* queue could not be created.
*/
Queue create() {
Queue queue = malloc(sizeof (struct Q));
if (queue == NULL) return NULL;
queue->head = NULL;
queue->tail = NULL;
queue->size = 0;
return queue;
}
/*
* Returns the size with a simple lookup.
*/
int size(Queue queue) {
return queue->size;
}
/*
* Adds a fresh new copy of the the given string to the queue.
*/
void enqueue(Queue queue, char* item) {
struct Node* node = malloc(sizeof (struct Node));
if (node == NULL) return;
node->item = strdup(item);
node->next = NULL;
if (queue->size == 0) {
queue->head = node;
} else {
queue->tail->next = node;
}
queue->tail = node;
queue->size++;
}
/*
* Removes the item from the front of the queue and returns
* it. Since copies of the items were made when they were
* added, we can just return a pointer to that copy. If the queue
* is empty, NULL is returned.
*/
char* dequeue(Queue queue) {
if (queue->size == 0) {
return NULL;
} else {
struct Node* headNode = queue->head;
char* result = headNode->item;
queue->head = queue->head->next;
free(headNode);
if (queue->size == 1) queue->tail = NULL;
queue->size--;
return result;
}
}
/*
* Returns 1 (true) if item is in queue, otherwise returns 0 (false).
*/
int contains(Queue queue, char* item) {
struct Node* p;
for (p = queue->head; p != NULL; p = p->next) {
if (strcmp(p->item, item) == 0) {
return 1;
}
}
return 0;
}
/*
* Cleans up any memory allocated when we created the queue,
* including the queue itself.
*/
void destroy(Queue queue) {
struct Node* node = queue->head;
while (node != NULL) {
struct Node* next = node->next;
free(node->item);
free(node);
node = next;
}
free(queue);
}
/*
* This application is a unit test for the Queue module.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "singly_linked_queue.h"
/*
* This helper dequeues a value and, if something was taken from
* the queue, checks that it compares equal to an expected value
* then frees it.
*/
void checkDequeue(Queue q, char* expectedValue) {
char* value = dequeue(q);
if (value == NULL) {
assert(expectedValue == NULL);
} else {
assert(strcmp(value, expectedValue) == 0);
free(value);
}
}
int main() {
char mutt[] = {'d', 'o', 'g', '\0'};
/* Check creation */
Queue q = create();
assert(size(q) == 0);
/* Check basic enqueueing, size, and contains */
enqueue(q, "dog");
assert(size(q) == 1);
enqueue(q, "cat");
assert(size(q) == 2);
assert(contains(q, "dog"));
assert(contains(q, mutt));
/* Check FIFO behavior */
checkDequeue(q, "dog");
assert(size(q) == 1);
assert(!contains(q, "dog"));
enqueue(q, "rat");
checkDequeue(q, "cat");
checkDequeue(q, "rat");
assert(size(q) == 0);
/* Check dequeueing from an empty queue */
checkDequeue(q, NULL);
checkDequeue(q, NULL);
assert(size(q) == 0);
assert(!contains(q, "dog"));
/* Check enqueuing a local char array */
enqueue(q, mutt);
checkDequeue(q, "dog");
assert(size(q) == 0);
/* Check destroy */
destroy(q);
/* Hope we get here */
printf("All tests passed\n");
return 0;
}
/*
* utf8_to_utf16be.c
*
* A filter (stdin -> stdout) that converts a utf8 encoded byte stream
* to the equivalent utf16be byte streeam. Only codepoints in the
* range 0..10FFFF are allowed; utf-8 "encodings" beyond this range
* are considered malformed.
*
* The return value of the process will be the number of "bad" bytes
* in the input stream, or -1 on an I/O error. When bad bytes
* are encountered during a read, they will be essentially ignored,
* meaning that we'll just move on to the next byte to try and find
* something legal. Only if the process returns 0 will the client
* know that the input was proper.
*
* This is an atrocious piece of code, by the way. It can be used
* as an illustration of many things not to do in code. Among
* the infractions and violations of good taste are:
*
* - the use of complex macros with break and continue statements
* - too much logic in main()
* - global variables to control flow
* - Hard-coded file handles for stdin and stdout
* - A hackish means to flush a buffer
* - failure to factor out utility code into a separate file (we
* shouldn't think in terms of a converter application, but
* rather in terms of a library of conversion routines called
* from various applications).
*/
/*
* Note we do NOT use the Standard C library for this application
* because there is no way to force stdin and stdout to be binary
* streams.
*/
#include <unistd.h>
typedef unsigned char Byte; /* a little more readable, I think */
#define BUFFER_SIZE 512 /* size of stream buffers */
/**
* All of the global variables and functions in this application are
* marked static to prevent other applications from linking to this
* code and using these things. With global variables holding state,
* this code isn't thread-safe anyway.
*/
static int error_count = 0; /* number of bad utf-8 encodings */
static int done = 0; /* global flag; quit when not 0 */
/**
* Reads the next byte from stdin. This function takes care of
* managing an input buffer so we don't need a system call to get
* each byte.
*/
static Byte get_next_byte() {
static Byte in[BUFFER_SIZE]; /* input file buffer */
static int i = 0; /* index of next slot to read from */
static int bytes_read;
if (i == 0) {
bytes_read = read(0, in, BUFFER_SIZE);
if (bytes_read == -1) return -1;
if (bytes_read == 0) done = 1;
}
Byte next_byte = in[i++];
if (i == bytes_read) i = 0; /* get ready to read again */
return next_byte;
}
/*
* Writes a byte to stdout. This function manages an internal output
* buffer, flushing it whenever it is full, or the global flag done
* is true.
*/
static void write_byte(Byte byte) {
static Byte out[BUFFER_SIZE]; /* output file buffer */
static int i = 0; /* index of next slot to write to */
if (!done) out[i++] = byte;
if (i == BUFFER_SIZE || done) {
write(1, out, i); /* flush... */
i = 0;
}
}
/*
* Checks an unsigned integer codepoint for validity and if it's
* good, writes its utf16 representation. This function assumes
* integers are at least 32 bytes in size.
*/
static void write_as_utf16(unsigned int codepoint) {
/* Noncharacters are errors */
if ((codepoint >= 0x0000fdd0 && codepoint <= 0x0000fdef)
|| codepoint > 0x0010ffff
|| (codepoint & 0x0000fffe) == 0x0000fffe) {
error_count++;
/* Surrogates are errors */
} else if (codepoint >= 0x0000d800 && codepoint <= 0x0000dfff) {
error_count++;
/* Codepoints in 0-ffff, written directly. */
} else if (codepoint <= 0x0000ffff) {
write_byte((codepoint & 0x0000ff00) >> 8);
write_byte(codepoint & 0x000000ff);
/* Codepoints beyond ffff, surrogates required */
} else {
unsigned difference = codepoint - 0x10000;
write_byte(0xd8 | ((difference & 0x000c0000) >> 18));
write_byte((difference & 0x0003fc00) >> 10);
write_byte(0xdc | ((difference & 0x00000300) >> 8));
write_byte(difference & 0x000000ff);
}
}
/*
* This is the most evil macro in the history of the world.
*/
#define GET_CONTINUTATION_BYTE(c) \
c = get_next_byte(); \
if (done) break; \
if (c & 0xc0 != 0x80) {error_count++; continue;}
/*
* Untested application.
*/
int main() {
while (1) {
Byte c1 = get_next_byte(), c2, c3, c4;
if (done) break;
/* One-byte utf8 (0-7f) */
if (c1 <= 0x7f) {
write_as_utf16(c1);
/* Two-byte utf8 (80-7ff) */
} else if (c1 & 0xe0 == 0xc0) {
GET_CONTINUTATION_BYTE(c2)
write_as_utf16((c1 & 0x1f) << 6 | c2 & 0x3f);
/* Three-byte utf8 (800-ffff) */
} else if (c1 & 0xF0 == 0xE0) {
GET_CONTINUTATION_BYTE(c2)
GET_CONTINUTATION_BYTE(c3)
write_as_utf16((c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | c3 & 0x3f);
/* Four-byte utf8, (10000-10ffff) */
} else if (c1 & 0xF8 == 0xF0) {
GET_CONTINUTATION_BYTE(c2)
GET_CONTINUTATION_BYTE(c3)
GET_CONTINUTATION_BYTE(c4)
write_as_utf16((c1 & 0x07) << 18 | (c2 & 0x3f) << 12
| (c3 & 0x3f) << 6 | (c4 & 0x3f));
/* Illegal starting byte, count error and skip */
} else {
error_count++;
continue;
}
}
/* Done processing, so flush the output buffer. Hackish! */
write_byte(0);
return error_count;
}
Common problems with many of the submissions: