I have been working through this problem for quite some time now and have succeeded in getting a partial mark. I would like to know what is wrong with the code that I have, that is preventing me from succeeding under certain conditions
I need one arduino to communicate with another one by sending a string of characters. So far I have succeeded in sending and receiving some data but think that I may be having an issue with the buffer I have set up in my uart_receive_string() function. I will provide all of the necessary information and code needed in order to test this, just let me know if any more info is required and Ill be happy to provide.
Here is a link to the tinkercad driver: https://www.tinkercad.com/things/eUZqkaIHp6J
Just click "Copy and Tinker" and hit the code button up top in order to paste the below code into it. You will need to paste the code into both ardunios by selecting them via the drop down box.
This is the criteria for the question I am working on:
This is the output I should receive in the test driver provided:
Here is the current code that I have implemented:
It is what needs to be copied into tinkercad for both arduino's
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void uart_putbyte(unsigned char data);
int uart_getbyte(unsigned char *buffer);
/*
** Define a function named uart_send_string which transmits the contents of
** a standard C string (i.e. a null-terminated char array) over UART. The
** function should iterate over the characters in the array, using a cast to
** convert each to an unsigned char, and transmitting the resulting byte via
** uart_putbyte. The end of the string should be signalled by sending a single
** null byte. That is, the number 0, not the character '0'.
**
** Param: str - string to be transmitted.
**
** Returns: Nothing.
*/
// vvvvvvv I need help with this vvvvvvv
void uart_send_string(char str[])
{
int i = 0;
char ch;
do{
ch = str[i];
uart_putbyte(ch);
i++;
}while(ch != '\0');
}
/*
** Define a function named uart_receive_string which uses uart_getbyte to fetch
** the contents of a standard C string (i.e. a null-terminated char array)
** from UART. The function should wait for characters, and must not return
** until a complete string has been retrieved.
**
** Note that uart_getbyte will return 1 if a byte is available, and zero
** otherwise. Therefore, to fetch a byte and store it in a variable named x,
** you will need to use a construct of the form:
** unsigned char x;
** while (! uart_getbyte(&x)) {
** // Do nothing.
** }
**
** Param: buffer - a char array which has capacity to store a string
** containing at most (buff_len-1) characters. If more than (buff_len-1)
** characters are received, the first (buff_len-1) of them should be
** stored consecutively in the buffer, and any others discarded. The
** string must be terminated correctly with a null terminator in all
** circumstances.
**
** Param: buff_len - an int which specifies the capacity of the buffer.
**
** Returns: Nothing. However, up to buff_len elements of buffer may have been
** overwritten by incoming data.
*/
//vvvvvvv I need help with this vvvvvvv
void uart_receive_string(char buffer[], int buff_len)
{
int i = 0;
unsigned char ch;
while(!uart_getbyte(&ch))
{
if(ch == 0)
{
break;
}
if(i < buff_len-1)
{
ch = buffer[i];
uart_putbyte(ch);
i++;
}
}
buffer[i]=0;
}
/*
***************************************************************************
** Initialise UART.
***************************************************************************
*/
void uart_init(void) {
UBRR0 = F_CPU / 16 / 9600 - 1;
UCSR0A = 0;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (3 << UCSZ00);
}
/*
**************************************************************************
** Send one byte, protecting against overrun in the transmit buffer.
**
** Param: data - a byte to be transmitted.
**
** Returns: Nothing.
***************************************************************************
*/
#ifndef __AMS__
void uart_putbyte(unsigned char data) {
// Wait for empty transmit buffer
while (!(UCSR0A & (1 << UDRE0)));
// Send data by assigning into UDR0
UDR0 = data;
}
#endif
/*
***************************************************************************
** Attempt to receive one byte, returning immediately to sender.
**
** Param: buffer - the address of a byte in which a result may be stored.
**
** Returns: If a byte is available returns 1 and stores the incoming byte in
** location referenced by buffer. Otherwise returns 0 and makes no other
** change to the state.
***************************************************************************
*/
#ifndef __AMS__
int uart_getbyte(unsigned char *buffer) {
// If receive buffer contains data...
if (UCSR0A & (1 << RXC0)) {
// Copy received byte from UDR0 into memory location (*buffer)
*buffer = UDR0;
//
return 1;
}
else {
return 0;
}
}
#endif
/*
***************************************************************************
** Implement main event loop.
***************************************************************************
*/
void process() {
// Use two devices, as indicated in the supplied TinkerCad model. One
// device acts as the sender (is_sender = 1), the other as receiver
// (is_sender = 0). Change this to set the role accordingly.
const int is_sender = 1;
if (is_sender) {
static char * messages_to_send[] = {
"", // Empty string
"A", // String with one symbol.
"Hello from CAB202!", // Multiple symbols
"1234567890abcdefghijklmnopqrstuvwxyz", // Longer than buffer size.
NULL, // End of list
};
static int next_message = 0;
uart_send_string(messages_to_send[next_message]);
next_message ++;
if (messages_to_send[next_message] == NULL) next_message = 0;
_delay_ms(300);
}
else {
#define BUFF_SIZE 20
char buffer[BUFF_SIZE];
uart_receive_string(buffer, BUFF_SIZE);
uart_send_string(buffer);
uart_putbyte('\r');
uart_putbyte('\n');
}
}
int main(void) {
uart_init();
while (1) {
process();
}
return 0;
}
The areas of this code that I am required to work on are these:
This is needed to send the data:
void uart_send_string(char str[])
{
int i = 0;
char ch;
do{
ch = str[i];
uart_putbyte(ch);
i++;
}while(ch != '\0');
}
This is needed to receive the data:
void uart_receive_string(char buffer[], int buff_len)
{
int i = 0;
unsigned char ch;
while(!uart_getbyte(&ch))
{
if(ch == 0)
{
break;
}
if(i < buff_len-1)
{
ch = buffer[i];
uart_putbyte(ch);
i++;
}
}
buffer[i]=0;
}
I am really sorry if this is hard to understand. Ill do my best to clarify any additional information that is needed. I just need to figure out what I am doing incorrectly.
Related
Documentation for pb_ostream_from_buffer says
After writing, you can check stream.bytes_written to find out how much
valid data there is in the buffer. This should be passed as the
message length on decoding side.
So ideally, when I send the serialized data I need to also send the bytes_written as a parameter separate from the buffer.
The problem is that my interface only allows me to send one variable: the buffer.
QUESTION
How do I specify always serialize the struct with no optimizations so that bufsize in
pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
can be a constant (i.e. the macro that specifies the maximum size) instead of needing to pass stream.bytes_written?
According to the Protocol Buffers encoding specification there are variable size types (like int32, int64, string, etc) and fixed size types (like fixed32, fixed64, double, etc). Now, this variable size encoding is more than just an optimization, it's a part of the design and specification. So disabling this "optimization" by the means of Protocol Buffers is only possible if your data consists exclusively of fixed length types and has no repeated fields as long as the number of repetitions is not fixed. I presume that this is not the case, since you're asking this question. So the short answer is no, it's not possible by means of the library because it would violate the encoding specification.
But in my opinion the desired effect could be easily achieved by encoding the size into the buffer with little CPU and RAM overhead. I presume you know the maximum size of the message generated by nanopb, we denote it by MAX_MSG_SIZE. We call this message the payload message. Suppose that this MAX_MSG_SIZE can be represented by some integer type, which we denote by wrapped_size_t (e.g. uint16_t).
The idea is simple:
allocate the buffer slightly larger than MAX_MSG_SIZE;
write the payload message generated by nanopb at some offset into the allocated buffer;
use this offset to encode the size of the payload message at the beginning of the buffer;
transmit the whole buffer having the fixed size equal to MAX_MSG_SIZE + sizeof(wrapped_size_t) to the receiver;
upon reception decode the size of the payload message and pass both the decoded size and the payload message to pb_istream_from_buffer.
I attach the code to illustrate the idea. I used an example from nanopb repository:
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"
//#define COMMON_ENDIANNES
#ifdef COMMON_ENDIANNES
#define encode_size encode_size_ce
#define decode_size decode_size_ce
#else
#define encode_size encode_size_le
#define decode_size decode_size_le
#endif
typedef uint16_t wrapped_size_t;
/* Maximum size of the message returned by bytes_written */
const size_t MAX_MSG_SIZE = 11;
/* Size of the field storing the actual size of the message
* (as returned by bytes_written) */
const size_t SIZE_FIELD = sizeof(wrapped_size_t);
/* Fixed wrapped message size */
const size_t FIXED_MSG_SIZE = MAX_MSG_SIZE + sizeof(wrapped_size_t);
void print_usage(char *prog);
/* Get the address of the payload buffer from the transmitted buffer */
uint8_t* payload_buffer(uint8_t *buffer);
/* Encode the payload size into the transmitted buffer (common endiannes) */
void encode_size_ce(uint8_t *buffer, size_t size);
/* Decode the payload size into the transmitted buffer (common endiannes) */
wrapped_size_t decode_size_ce(uint8_t *buffer);
/* Encode the payload size into the transmitted buffer (little endian) */
void encode_size_le(uint8_t *buffer, size_t size);
/* Decode the payload size into the transmitted buffer (little endian) */
size_t decode_size_le(uint8_t *buffer);
int main(int argc, char* argv[])
{
/* This is the buffer where we will store our message. */
uint8_t buffer[MAX_MSG_SIZE + sizeof(wrapped_size_t)];
bool status;
if(argc > 2 || (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))))
{
print_usage(argv[0]);
return 1;
}
/* Encode our message */
{
/* Allocate space on the stack to store the message data.
*
* Nanopb generates simple struct definitions for all the messages.
* - check out the contents of simple.pb.h!
* It is a good idea to always initialize your structures
* so that you do not have garbage data from RAM in there.
*/
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(payload_buffer(buffer),
MAX_MSG_SIZE);
if(argc > 1)
sscanf(argv[1], "%" SCNd32, &message.lucky_number);
else
{
printf("Input lucky number: ");
scanf("%" SCNd32, &message.lucky_number);
}
/* Encode the payload message */
status = pb_encode(&stream, SimpleMessage_fields, &message);
/* Wrap the payload, i.e. add the size to the buffer */
encode_size(buffer, stream.bytes_written);
/* Then just check for any errors.. */
if (!status)
{
printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
}
/* Now we could transmit the message over network, store it in a file, etc.
* Note, the transmitted message has a fixed length equal to FIXED_MSG_SIZE
* and is stored in buffer
*/
/* But for the sake of simplicity we will just decode it immediately. */
{
/* Allocate space for the decoded message. */
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(payload_buffer(buffer),
decode_size(buffer));
/* Now we are ready to decode the message. */
status = pb_decode(&stream, SimpleMessage_fields, &message);
/* Check for errors... */
if (!status)
{
printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Print the data contained in the message. */
printf("Your lucky number was %d; payload length was %d.\n",
(int)message.lucky_number, (int)decode_size(buffer));
}
return 0;
}
void print_usage(char *prog)
{
printf("usage: %s [<lucky_number>]\n", prog);
}
uint8_t* payload_buffer(uint8_t *buffer)
{
return buffer + SIZE_FIELD;
}
void encode_size_ce(uint8_t *buffer, size_t size)
{
*(wrapped_size_t*)buffer = size;
}
wrapped_size_t decode_size_ce(uint8_t *buffer)
{
return *(wrapped_size_t*)buffer;
}
void encode_size_le(uint8_t *buffer, size_t size)
{
int i;
for(i = 0; i < sizeof(wrapped_size_t); ++i)
{
buffer[i] = size;
size >>= 8;
}
}
size_t decode_size_le(uint8_t *buffer)
{
int i;
size_t ret = 0;
for(i = sizeof(wrapped_size_t) - 1; i >= 0; --i)
ret = buffer[i] + (ret << 8);
return ret;
}
UPD Ok, if, for some reason, you still wish to stick to the original GPB encoding there's another option available: fill the unused part of the buffer (i.e. the part after the last byte written by nanopb) with some valid data which will be ignored. For instance, you can reserve a field number which doesn't mark any field in your *.proto file but is used to mark the data which will be discarded by the GPB decoder. Let's denote this reserved field number as RESERVED_FIELD_NUMBER. This is used for backward compatibility but you can use it for your purpose as well. Let's call this filling-in the buffer with the dummy data sealing (perhaps there's a better term). This method also requires that you have at least 2 free bytes available to you after pb_encode.
So the idea of sealing is even simpler:
calculate how many buffer bytes is left unfilled after pb_encode;
mark the rest of the buffer as array of bytes with RESERVED_FIELD_NUMBER.
I attach the updated code, the main function is bool seal_buffer(uint8_t *buffer, size_t size), call it after pb_encode to seal the buffer and you're done. Currently, it has a limitation of sealing no more than 2 ** 28 + 4 bytes, but it could be easily updated to overcome this limitation.
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"
/* Reserved field_number shouldn't be used for field numbering. We use it
* to mark the data which will be ignored upon reception by GPB parser.
* This number should be 1 to 15 to fit into a single byte. */
const uint8_t RESERVED_FIELD_NUMBER = 15;
/* Maximum size of the message returned by bytes_written (payload size) */
const size_t MAX_MSG_SIZE = 200;
/* Size of the transmitted message (reserve 2 bytes for minimal sealing) */
const size_t FIXED_MSG_SIZE = MAX_MSG_SIZE + 2;
void print_usage(char *prog);
/* Sealing the buffer means filling it in with data which is valid
* in the sense that a GPB parser accepts it as valid but ignores it */
bool seal_buffer(uint8_t *buffer, size_t size);
int main(int argc, char* argv[])
{
/* This is the buffer where we will store our message. */
uint8_t buffer[FIXED_MSG_SIZE];
bool status;
if(argc > 2 || (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))))
{
print_usage(argv[0]);
return 1;
}
/* Encode our message */
{
/* Allocate space on the stack to store the message data.
*
* Nanopb generates simple struct definitions for all the messages.
* - check out the contents of simple.pb.h!
* It is a good idea to always initialize your structures
* so that you do not have garbage data from RAM in there.
*/
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if(argc > 1)
sscanf(argv[1], "%" SCNd32, &message.lucky_number);
else
{
printf("Input lucky number: ");
scanf("%" SCNd32, &message.lucky_number);
}
/* Now we are ready to encode the message! */
status = pb_encode(&stream, SimpleMessage_fields, &message);
/* Then just check for any errors.. */
if (!status)
{
fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Now the main part - making the buffer fixed-size */
assert(stream.bytes_written + 2 <= FIXED_MSG_SIZE);
if(!seal_buffer(buffer + stream.bytes_written,
FIXED_MSG_SIZE - stream.bytes_written))
{
fprintf(stderr, "Failed sealing the buffer "
"(filling in with valid but ignored data)\n");
return 1;
}
}
/* Now we could transmit the message over network, store it in a file or
* wrap it to a pigeon's leg.
*/
/* But because we are lazy, we will just decode it immediately. */
{
/* Allocate space for the decoded message. */
SimpleMessage message = SimpleMessage_init_zero;
/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(buffer, FIXED_MSG_SIZE);
/* Now we are ready to decode the message. */
status = pb_decode(&stream, SimpleMessage_fields, &message);
/* Check for errors... */
if (!status)
{
fprintf(stderr, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
/* Print the data contained in the message. */
printf("Your lucky number was %d.\n", (int)message.lucky_number);
}
return 0;
}
void print_usage(char *prog)
{
printf("usage: %s [<lucky_number>]\n", prog);
}
bool seal_buffer(uint8_t *buffer, size_t size)
{
size_t i;
if(size == 1)
{
fprintf( stderr, "Cannot seal the buffer, at least 2 bytes are needed\n");
return false;
}
assert(size - 5 < 1<<28);
if(size - 5 >= 1<<28)
{
fprintf( stderr, "Representing the size exceeding 2 ** 28 + 4, "
"although it's not difficult, is not yet implemented\n");
return false;
}
buffer[0] = (15 << 3) + 2;
/* encode the size */
if(size - 2 < 1<<7)
buffer[1] = size - 2;
else
{
/* Size is large enough to fit into 7 bits (1 byte).
* For simplicity we represent the remaining size by 4 bytes (28 bits).
* Note that 1 byte is used for encoding field_number and wire_type,
* plus 4 bytes for the size encoding, therefore the "remaining size"
* is equal to (size - 5)
*/
size -= 5;
for(i = 0; i < 4; ++i)
{
buffer[i + 1] = i < 3? (size & 0x7f) | 0x80: size & 0x7f;
size >>= 7;
}
}
return true;
}
I am trying to solve a "suppose to be" a simple C question.
Q: Receive "infinite" input by user using int pointer until EOF received using malloc and realloc.
I defined a int pointer like this:
int *inputBuffer = NULL;
and initialized it using this method:
/* #define BUFFER_SIZE 8 - already defined at top of code, posted here for more info */
/* Creates buffer by using malloc -
if succeded returns true, otherwise false */
int createBuffer(int **inputBuffer)
{
*inputBuffer = (int*) calloc(BUFFER_SIZE, sizeof(char)); /* Allocate memory to get input from user */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
by calling createBuffer(&inputBuffer)
so far so good, memory is allocated successfully.
before starting to receive characters from user I defined the following properties:
int totalCharacters = 0;
int bufferExtendsCounter = 1;
int character;
Next step is to receive characters inputs from user like this:
/* #define MEMORY_SAFE_GAP 4 - already defined at top of code, posted here for more info */
while((character = getchar()) != EOF && inputBuffer != NULL)
{
/* Check if reallocate needs to be called since almost maxed out buffer */
if(totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
{
/* Add 1 to extends buffer times */
bufferExtendsCounter+=1;
if(!extendBuffer(&inputBuffer, totalCharacters))
printf("\nFailed to allocate more memory.");
}
/* Initialize buffer with character, this is safe since there is a memory safe gap */
inputBuffer[totalCharacters] = character;
totalCharacters++;
}
extend buffer looks like this:
/* Extends buffer size by using realloc
if succeded returns true, otherwise false */
int extendBuffer(int **inputBuffer, int minimumBufferSize)
{
/* Check how many times buffer needs to be multiple (at least) */
int multipleBufferNumber = (minimumBufferSize / BUFFER_SIZE) + 1;
int newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
while(newBufferSize < minimumBufferSize)
{
multipleBufferNumber+=1;
newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
}
/* Reallocate memory for next chunck of data */
*inputBuffer = realloc(*inputBuffer, newBufferSize);
/* Check if memory successfully allocated */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
It looks like I extend the buffer size enough for more input by user, but still gets error:
corrupted size vs. prev_size: 0x08f86010 ***
Example input:
TestProgramTest (Pressed Enter after last 't')
(DebugPrint: Received 13 characters)
(DebugPrint: Reallocating to size 16)
*** Error in `./test': corrupted size vs. prev_size: 0x08f86010 ***
EDIT (Due to lack of code parts):
The following part is right after while loop:
inputBuffer[totalCharacters] = '\0';
printf("\nInput by user:\n");
/* #define LINE_LENGTH 5 - already defined at top of code, posted here for more info */
printBuffer(inputBuffer, LINE_LENGTH, totalCharacters);
/* free memory */
free(inputBuffer);
and printBuffer looks like:
/* Prints the buffer data to pretty output */
void printBuffer(int *inputBuffer, int lineLength, int totalCharacters)
{
int i;
for(i = 0; i < totalCharacters; i++)
{
/* Print each char value */
printf("%c", inputBuffer[i]);
/* Check if got to line limit, if so enter new line */
if((i+1) % lineLength == 0)
printf("\n");
}
}
Second edit:
Changed all int pointer parts to char pointer.
Full code looks like:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#define LINE_LENGTH 5
#define BUFFER_SIZE 8
#define TRUE 1
#define FALSE 0
#define MEMORY_SAFE_GAP 4
int createBuffer(char **inputBuffer);
int extendBuffer(char **inputBuffer, int minimumBufferSize);
void printBuffer(char *inputBuffer, int lineLength, int totalCharacters);
int main(void)
{
char *inputBuffer = NULL;
if(!createBuffer(&inputBuffer))
{
printf("Memory cannot be allocated, program will exit now.");
exit(-1);
}
int totalCharacters = 0;
int bufferExtendsCounter = 1;
char character;
printf("Please enter a string:\n");
/* Loop till EOF received */
while((character = getchar()) != EOF && inputBuffer != NULL)
{
/* Check if reallocate needs to be called since almost maxed out buffer */
if(totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
{
/* Add 1 to extends buffer times */
bufferExtendsCounter+=1;
if(!extendBuffer(&inputBuffer, totalCharacters))
printf("\nFailed to allocate more memory.");
}
/* Initialize buffer with character, this is safe since there is a memory safe gap */
inputBuffer[totalCharacters] = character;
totalCharacters++;
}
inputBuffer[totalCharacters] = '\0';
printBuffer(inputBuffer, LINE_LENGTH, totalCharacters);
/* free memory */
free(inputBuffer);
return 0;
}
/* Creates buffer by using malloc
if succeded returns true, otherwise false */
int createBuffer(char **inputBuffer)
{
/* Allocate memory to get input from user */
*inputBuffer = (char*) calloc(BUFFER_SIZE, sizeof(char));
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
/* Extends buffer size by using realloc
if succeded returns true, otherwise false */
int extendBuffer(char **inputBuffer, int minimumBufferSize)
{
/* Check how many times buffer needs to be multiple (at least) */
int multipleBufferNumber = (minimumBufferSize / BUFFER_SIZE) + 1;
int newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
while(newBufferSize < minimumBufferSize)
{
multipleBufferNumber+=1;
newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
}
/* Reallocate memory for next chunck of data */
*inputBuffer = realloc(*inputBuffer, newBufferSize);
/* Check if memory successfully allocated */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
/* Prints the buffer data to pretty output */
void printBuffer(char *inputBuffer, int lineLength, int totalCharacters)
{
printf("Printing buffer\n");
int i;
for(i = 0; i < totalCharacters; i++)
{
/* Print each char value */
printf("%c", inputBuffer[i]);
/* Check if got to line limit, if so enter new line */
if((i+1) % lineLength == 0)
printf("\n");
}
}
Any help would be great!
Thanks in advance.
Right here
*inputBuffer = (int*) calloc(BUFFER_SIZE, sizeof(char));
You reserve space for 8 chars but try to store 8 ints in it
Why isnt inputBuffer just a char*? since thats what you are storing
Now you have fixed that - look at this
if (totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
I do not know what the intention of the 'MEMORY_SAFE_GAP' is but its wrong
Look at what happens when I input character number 8
if(8 - 4 > 8 * 1)
is false, so you do not extend the buffer.
This 'SAFE-GAP ensure that you always run off the end, your code no longer crashes if you just have
if (totalCharacters >= (BUFFER_SIZE * bufferExtendsCounter))
output is still a little garbled but you can probably fix that. I input 1234567890 and got
Please enter a string:
1234567890
^Z
Printing buffer
12345
678═0
I am looking to alter the LZW compressor to enable it to search for a word in an LZW encoded file and finds the number of matches for that search term.
For example if my file is used as
Prompt:>lzw "searchterm" encoded_file.lzw
32
Any suggestions on how to achive this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BITS 12 /* Setting the number of bits to 12, 13*/
#define HASHING_SHIFT (BITS-8) /* or 14 affects several constants. */
#define MAX_VALUE (1 << BITS) - 1 /* Note that MS-DOS machines need to */
#define MAX_CODE MAX_VALUE - 1 /* compile their code in large model if*/
/* 14 bits are selected. */
#if BITS == 16
#define TABLE_SIZE 99991
#endif
#if BITS == 14
#define TABLE_SIZE 18041 /* The string table size needs to be a */
#endif /* prime number that is somewhat larger*/
#if BITS == 13 /* than 2**BITS. */
#define TABLE_SIZE 9029
#endif
#if BITS <= 12
#define TABLE_SIZE 5021
#endif
void *malloc();
int *code_value; /* This is the code value array */
unsigned int *prefix_code; /* This array holds the prefix codes */
unsigned char *append_character; /* This array holds the appended chars */
unsigned char decode_stack[4000]; /* This array holds the decoded string */
/*
* Forward declarations
*/
void compress(FILE *input,FILE *output);
void expand(FILE *input,FILE *output);
int find_match(int hash_prefix,unsigned int hash_character);
void output_code(FILE *output,unsigned int code);
unsigned int input_code(FILE *input);
unsigned char *decode_string(unsigned char *buffer,unsigned int code);
/********************************************************************
**
** This program gets a file name from the command line. It compresses the
** file, placing its output in a file named test.lzw. It then expands
** test.lzw into test.out. Test.out should then be an exact duplicate of
** the input file.
**
*************************************************************************/
main(int argc, char *argv[])
{
FILE *input_file;
FILE *output_file;
FILE *lzw_file;
char input_file_name[81];
char command;
command=(argv==3);
/*
** The three buffers are needed for the compression phase.
*/
code_value=(int*)malloc(TABLE_SIZE*sizeof(int));
prefix_code=(unsigned int *)malloc(TABLE_SIZE*sizeof(unsigned int));
append_character=(unsigned char *)malloc(TABLE_SIZE*sizeof(unsigned char));
if (code_value==NULL || prefix_code==NULL || append_character==NULL)
{
printf("Fatal error allocating table space!\n");
exit(-1);
}
/*
** Get the file name, open it up, and open up the lzw output file.
*/
if (argc>1)
strcpy(input_file_name,argv[1]);
else
{
printf("Input file name? ");
scanf("%s",input_file_name);
}
input_file=fopen(input_file_name,"rb");
lzw_file=fopen("test.lzw","wb");
if (input_file==NULL || lzw_file==NULL)
{
printf("Fatal error opening files.\n");
exit(-1);
};
/*
** Compress the file.
*/
if(command=='r')
{
compress(input_file,lzw_file);
}
fclose(input_file);
fclose(lzw_file);
free(c-ode_value);
/*
** Now open the files for the expansion.
*/
lzw_file=fopen("test.lzw","rb");
output_file=fopen("test.out","wb");
if (lzw_file==NULL || output_file==NULL)
{
printf("Fatal error opening files.\n");
exit(-2);
};
/*
** Expand the file.
*/
expand(lzw_file,output_file);
fclose(lzw_file);
fclose(output_file);
free(prefix_code);
free(append_character);
}
/*
** This is the compression routine. The code should be a fairly close
** match to the algorithm accompanying the article.
**
*/
void compress(FILE *input,FILE *output)
{
unsigned int next_code;
unsigned int character;
unsigned int string_code;
unsigned int index;
int i;
next_code=256; /* Next code is the next available string code*/
for (i=0;i<TABLE_SIZE;i++) /* Clear out the string table before starting */
code_value[i]=-1;
i=0;
printf("Compressing...\n");
string_code=getc(input); /* Get the first code */
/*
** This is the main loop where it all happens. This loop runs util all of
** the input has been exhausted. Note that it stops adding codes to the
** table after all of the possible codes have been defined.
*/
while ((character=getc(input)) != (unsigned)EOF)
{
if (++i==1000) /* Print a * every 1000 */
{ /* input characters. This */
i=0; /* is just a pacifier. */
printf("*");
}
index=find_match(string_code,character);/* See if the string is in */
if (code_value[index] != -1) /* the table. If it is, */
string_code=code_value[index]; /* get the code value. If */
else /* the string is not in the*/
{ /* table, try to add it. */
if (next_code <= MAX_CODE)
{
code_value[index]=next_code++;
prefix_code[index]=string_code;
append_character[index]=character;
}
output_code(output,string_code); /* When a string is found */
string_code=character; /* that is not in the table*/
} /* I output the last string*/
} /* after adding the new one*/
/*
** End of the main loop.
*/
output_code(output,string_code); /* Output the last code */
output_code(output,MAX_VALUE); /* Output the end of buffer code */
output_code(output,0); /* This code flushes the output buffer*/
printf("\n");
}
/*
** This is the hashing routine. It tries to find a match for the prefix+char
** string in the string table. If it finds it, the index is returned. If
** the string is not found, the first available index in the string table is
** returned instead.
*/
int find_match(int hash_prefix,unsigned int hash_character)
{
int index;
int offset;
index = (hash_character << HASHING_SHIFT) ^ hash_prefix;
if (index == 0)
offset = 1;
else
offset = TABLE_SIZE - index;
while (1)
{
if (code_value[index] == -1)
return(index);
if (prefix_code[index] == hash_prefix &&
append_character[index] == hash_character)
return(index);
index -= offset;
if (index < 0)
index += TABLE_SIZE;
}
}
/*
** This is the expansion routine. It takes an LZW format file, and expands
** it to an output file. The code here should be a fairly close match to
** the algorithm in the accompanying article.
*/
void expand(FILE *input,FILE *output)
{
unsigned int next_code;
unsigned int new_code;
unsigned int old_code;
int character;
int counter;
unsigned char *string;
next_code=256; /* This is the next available code to define */
counter=0; /* Counter is used as a pacifier. */
printf("Expanding...\n");
old_code=input_code(input); /* Read in the first code, initialize the */
character=old_code; /* character variable, and send the first */
putc(old_code,output); /* code to the output file */
/*
** This is the main expansion loop. It reads in characters from the LZW file
** until it sees the special code used to inidicate the end of the data.
*/
while ((new_code=input_code(input)) != (MAX_VALUE))
{
if (++counter==1000) /* This section of code prints out */
{ /* an asterisk every 1000 characters */
counter=0; /* It is just a pacifier. */
printf("*");
}
/*
** This code checks for the special STRING+CHARACTER+STRING+CHARACTER+STRING
** case which generates an undefined code. It handles it by decoding
** the last code, and adding a single character to the end of the decode string.
*/
if (new_code>=next_code)
{
*decode_stack=character;
string=decode_string(decode_stack+1,old_code);
}
/*
** Otherwise we do a straight decode of the new code.
*/
else
string=decode_string(decode_stack,new_code);
/*
** Now we output the decoded string in reverse order.
*/
character=*string;
while (string >= decode_stack)
putc(*string--,output);
/*
** Finally, if possible, add a new code to the string table.
*/
if (next_code <= MAX_CODE)
{
prefix_code[next_code]=old_code;
append_character[next_code]=character;
next_code++;
}
old_code=new_code;
}
printf("\n");
}
/*
** This routine simply decodes a string from the string table, storing
** it in a buffer. The buffer can then be output in reverse order by
** the expansion program.
*/
unsigned char *decode_string(unsigned char *buffer,unsigned int code)
{
int i;
i=0;
while (code > 255)
{
*buffer++ = append_character[code];
code=prefix_code[code];
if (i++>=MAX_CODE)
{
printf("Fatal error during code expansion.\n");
exit(-3);
}
}
*buffer=code;
return(buffer);
}
/*
** The following two routines are used to output variable length
** codes. They are written strictly for clarity, and are not
** particularyl efficient.
*/
unsigned int input_code(FILE *input)
{
unsigned int return_value;
static int input_bit_count=0;
static unsigned long input_bit_buffer=0L;
while (input_bit_count <= 24)
{
input_bit_buffer |=
(unsigned long) getc(input) << (24-input_bit_count);
input_bit_count += 8;
}
return_value=input_bit_buffer >> (32-BITS);
input_bit_buffer <<= BITS;
input_bit_count -= BITS;
return(return_value);
}
void output_code(FILE *output,unsigned int code)
{
static int output_bit_count=0;
static unsigned long output_bit_buffer=0L;
output_bit_buffer |= (unsigned long) code << (32-BITS-output_bit_count);
output_bit_count += BITS;
while (output_bit_count >= 8)
{
putc(output_bit_buffer >> 24,output);
output_bit_buffer <<= 8;
output_bit_count -= 8;
}
}
Here's a document on an algorithm to do regex searching directly in LZW compressed bytes :
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.9.1434&rep=rep1&type=pdf
It contains references to efficient algorithms to search for exact strings as well.
#include <reg51.h>
#include "_LCD_R8C.c"
#define INPUT_LENGTH 11
char input[INPUT_LENGTH]; /* The input from the serial port */
int input_pos = 0; /* Current position to write in the input buffer */
int main()
{
int i;
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20; /* timer 1, mode 2, 8-bit reload */
TH1 = 0xFD; /* reload value for 2400 baud */
TR1 = 1;
TI = 1;
RI = 1;
while (1 == 1)
{
/* read the next character from the serial port */
input[input_pos++] = getCharacter ();
/* send it back to the original sender */
for (i = 0; i <= input_pos; i++)
{
lcd_print_b(input[i]);
}
}
}
char getCharacter(void)
{
char chr[INPUT_LENGTH]; /* variable to hold the new character */
while (RI != 1) {;}
chr[input_pos++] = SBUF;
RI = 0;
return (chr);
}
I tried displaying the no's which I am receiving from rs232 which is read by the rfreader.
but I am getting the wrong value on the display i.e 002100 instead of 0016221826. but on the hyper terminal I am getting the exact correct value with a $ included at the satrting i.e $0016221826.
First of all, you really need to adopt a sane indention style, this code is very hard to read.
The problem with your code is that you read an array of user input into a local array "chr", then return the address of that array to main, instead of a character. main() does not expect an address, it expects a character. And regardless of that, the array "chr" is invalid once you have left the function anyhow.
Your printing for loop is also incorrect and doesn't make any sense. You keep printing all characters, over and over, each time you receive a new one.
There might be other issues with the hardware or MCU, I just fixed the most obvious software bugs.
#include <reg51.h>
#include "_LCD_R8C.c"
#define INPUT_LENGTH 11
int main()
{
char input[INPUT_LENGTH]; /* The input from the serial port */
int input_pos = 0; /* Current position to write in the input buffer */
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20; /* timer 1, mode 2, 8-bit reload */
TH1 = 0xFD; /* reload value for 2400 baud */
TR1 = 1;
TI = 1;
RI = 1;
while(1)
{
/* read the next character from the serial port */
if(input_pos < INPUT_LENGTH) /* check for buffer overflow */
{
input[input_pos] = getCharacter();
lcd_print_b(input[input_post]); /* only makes sense to print each character once */
input_pos++;
}
}
char getCharacter (void)
{
char chr /* variable to hold the new character */
while (RI != 1)
;
chr = SBUF;
RI = 0;
return(chr);
}
#include<reg51.h>
#include<string.h>
#include"_LCD_R8C.c"
unsigned char c[12];
unsigned char chr[11];
void serial_int (void) interrupt 4
{
if (RI==1)
{
chr[11] = SBUF;
RI = 0;
TI = 0;
}
}
int main()
{
unsigned char a[2][11]={"$0016221826","$0123456789"};
int i,j;
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20;
TH1 = 0xFD;
ET0 = 0;
TR1 = 1;
RI = 1;
ES = 1;
EA = 1;
for(j=0;j<1;j++)
{
for(i=0;i<=10;i++)
{
c[i]=chr[i];
}
c[11]='\0';
}
for(i=0;i<=1;i++)
{
j=strcmp(a[i],c); /* !!! Here is the problem !!! */
if(j==0)
{
lcd_printxy(1,1,"yes");
}
else
{
lcd_printxy(1,6,"no");
}
}
}
I am getting the display as "no", please let me know what is the problem?
the problem might be
1) the received array of characters are not converted to string, or
2) the received array of characters are converted to string but not able to compare with the available string..
please go through the program
One obvious bug for starters - change:
unsigned char a[2][11]={"$0016221826","$0123456789"};
to:
unsigned char a[2][12]={"$0016221826","$0123456789"};
(You need to allow room for the terminating '\0' in each string - I'm surprised your compiler didn't complain about this ?)
Also, this line in your interrupt handler is wrong:
chr[11] = SBUF;
Several problems with this - char only has storage for 11 chars, not 12, and you probably want to be accumulating characters from index 0 and then bumping the index, otherwise you're just overwriting the same character each time.
Looking at the rest of the code there are so many other problems that I think you may need to take a step back here and start with a simpler program - get that working first and then add to it in stages.
You might also want to get a decent introductory book on C and study it as there are lots of very basic mistakes in the code, so you might benefit from a better understanding of the language itself.
You only assign a value to chr[11], the rest of the array is uninitialized and will contain random data. You then copy this array containing random data to c (you could use e.g. memcpy here instead of looping yourself), and finally you compare the complete contents of c (which is random data) with one of the entries in a. So it's kind of natural that the result of that comparison will be that the strings are not equal.
Edit: A redesign of the program in the question
Your program has too many problems to be easily fixed, so I decided to try and rewrite it:
#include <reg51.h>
#include <string.h>
#include "_LCD_R8C.c"
#define INPUT_LENGTH 11
#define ACCEPTABLE_INPUT_COUNT 2
char input[INPUT_LENGTH]; /* The input from the serial port */
int input_pos = 0; /* Current position to write in the input buffer */
int input_done = 0; /* 0 = not done yet, 1 = all input read */
void serial_int (void) interrupt 4
{
if (!input_done && RI == 1)
{
/* Put the input at next position in the input buffer */
/* Then increase the position */
input[input_pos++] = SBUF;
RI = 0;
TI = 0;
/* Check if we have received all input yet */
if (input_pos >= INPUT_LENGTH)
input_done = 1;
}
}
int main()
{
/* Array of data that this programs thinks is acceptable */
/* +1 to the input length, to fit the terminating '\0' character */
char acceptable_inputs[ACCEPTABLE_INPUT_COUNT][INPUT_LENGTH + 1] = {
"$0016221826", "$0123456789"
};
iny acceptable_found = 0; /* Acceptable input found? */
/* Initialization */
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20;
TH1 = 0xFD;
ET0 = 0;
TR1 = 1;
RI = 1;
ES = 1;
EA = 1;
/* Wait until we have received all input */
while (!input_done)
; /* Do nothing */
/* Check the received input for something we accept */
for (int i = 0; i < ACCEPTABLE_INPUT_COUNT; i++)
{
if (memcmp(acceptable_inputs[i], input, INPUT_LENGTH) == 0)
{
/* Yes, the data received is acceptable */
acceptable_found = 1;
break; /* Don't have to check any more */
}
}
if (acceptable_found)
lcd_printxy(1, 1, "Yes");
else
lcd_printxy(1, 1, "No");
return 0;
}