Trying to use SetConsoleScreenBufferSize but it fails and puts "The handle is invalid." In the last error. Will post all code, but here are some highlights:
Using this to resize buffer:
int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
COORD bufferNewSize = {
size.dwSize.X,
size.dwSize.Y
};
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
}
}
Using this to allocate handle:
struct TGHandle TG() {
struct TGHandle tgHandle;
tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
// Create the input buffer
tgHandle.inputBufferSize = 32;
tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
// Hook up the input handle
tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
return tgHandle;
}
Here is full code.
tg.h
#ifndef TG_H
#define TG_H
#include <Windows.h>
#include <memory.h>
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define BACKGROUND_WHITE BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
// A drawing buffer, for general purposes
struct TGDrawBuffer {
COORD size;
CHAR_INFO *buffer;
};
struct TGDrawBuffer createTGDrawBuffer(int, int); // Function to allocate a drawing buffer
void sizeTGDrawBuffer(struct TGDrawBuffer*, int, int); // Resize a draw buffer
void clearTGDrawBuffer(struct TGDrawBuffer*); // Fill a buffer with blank cells
void TGDrawPixel(struct TGDrawBuffer*, int, int, CHAR_INFO); // Draw to a single cell on the buffer
void TGDrawAttribute(struct TGDrawBuffer*, int, int, int); // Modify a single attribute. X, Y, Attr
void TGDrawCharInfoString(struct TGDrawBuffer*, int, int, CHAR_INFO*, int); // X, Y, string, int. Draws to max X
CHAR_INFO* TGCharToCharInfo(char*, int); // Convert basic characters to CHAR_INFO. String, length.
void TGDrawString(struct TGDrawBuffer*, int, int, char*, int); // X, Y, string, length. Draws to max X
void freeTGDrawBuffer(struct TGDrawBuffer*); // Function to de-allocate a drawing buffer
int CharInfoStrlen(CHAR_INFO*); // Get length of a CHAR_INFO as if it were a string
// Essentially a drawing context to the screen
struct TGHandle {
HANDLE screenBufferHandle, inputHandle;
struct TGDrawBuffer drawBuffer;
INPUT_RECORD *inputBuffer;
int inputBufferSize;
};
struct TGHandle TG(); // Initialization function, which returns a drawing context to the screen
void useTGHandle(struct TGHandle*); // Make a screen drawing context active
void updateTGHandle(struct TGHandle*); // Displays what has been drawn
void setTGHandleCursorVisibility(struct TGHandle*, int); // True / False
int getTGInput(struct TGHandle*, INPUT_RECORD*, int); // Fill input into a buffer
int getTGNextInput(struct TGHandle*, INPUT_RECORD*); // Get a single INPUT_RECORD or return false
int TGHandleResizeEvent(struct TGHandle*, INPUT_RECORD); // Resize is not handled automatically
#endif
tg.c
#include "tg.h"
#include <string.h>
struct TGDrawBuffer createTGDrawBuffer(int width, int height) {
struct TGDrawBuffer tgDrawBuffer;
tgDrawBuffer.buffer = NULL; // Init the buffer to NULL
sizeTGDrawBuffer(&tgDrawBuffer, width, height);
return tgDrawBuffer;
}
void sizeTGDrawBuffer(struct TGDrawBuffer* drawBuffer, int width, int height) {
// Using free/ malloc here because we aren't interested in retaining data
if (drawBuffer->buffer) {
free(drawBuffer->buffer);
}
drawBuffer->buffer = malloc(sizeof(CHAR_INFO) * (width * height));
// Copy the size to the buffer record
drawBuffer->size.X = width;
drawBuffer->size.Y = height;
}
void clearTGDrawBuffer(struct TGDrawBuffer *tgBuffer) {
int i = 0, limit = tgBuffer->size.X * tgBuffer->size.Y;
// Create a blank CHAR_INFO
CHAR_INFO clearChar;
clearChar.Char.AsciiChar = ' ';
clearChar.Char.UnicodeChar = ' ';
clearChar.Attributes = FOREGROUND_WHITE; // Would be confusing without this
// Set everything to that buffer
while (i < limit) {
tgBuffer->buffer[i] = clearChar;
i++;
}
}
void TGDrawPixel(struct TGDrawBuffer *tgBuffer, int x, int y, CHAR_INFO character) {
tgBuffer->buffer[(tgBuffer->size.X * y) + x] = character;
}
void TGDrawAttribute(struct TGDrawBuffer *tgBuffer, int x, int y, int attr) {
tgBuffer->buffer[(tgBuffer->size.X * y) + x].Attributes = attr;
}
void TGDrawCharInfoString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, CHAR_INFO *string, int length) {
int charsToWrite = length;
int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
if (distanceToEnd < charsToWrite)
distanceToEnd = charsToWrite;
int startPos = x + (tgDrawBuffer->size.X * y);
int i = 0;
while (i < distanceToEnd) {
tgDrawBuffer->buffer[startPos + x] = string[i];
i++;
}
}
CHAR_INFO* TGCharToCharInfo(char* string, int length) {
if (length == -1)
length = strlen(string);
// TODO
}
void TGDrawString(struct TGDrawBuffer *tgDrawBuffer, int x, int y, char *string, int length) {
int charsToWrite = length;
int distanceToEnd = (tgDrawBuffer->size.Y - 1) - y;
if (distanceToEnd < charsToWrite)
charsToWrite = distanceToEnd;
int startPos = x + (tgDrawBuffer->size.X * y);
int i = 0;
while (i < charsToWrite) {
tgDrawBuffer->buffer[startPos + i].Char.AsciiChar = string[i];
tgDrawBuffer->buffer[startPos + i].Char.UnicodeChar = string[i];
i++;
}
}
void freeTGDrawBuffer(struct TGDrawBuffer *drawBuffer) {
free(drawBuffer->buffer);
}
struct TGHandle TG() {
struct TGHandle tgHandle;
tgHandle.screenBufferHandle = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle.screenBufferHandle, &info);
tgHandle.drawBuffer = createTGDrawBuffer(info.dwSize.X, info.dwSize.Y);
// Create the input buffer
tgHandle.inputBufferSize = 32;
tgHandle.inputBuffer = malloc(sizeof(INPUT_RECORD) * tgHandle.inputBufferSize);
// Hook up the input handle
tgHandle.inputHandle = GetStdHandle(STD_INPUT_HANDLE);
return tgHandle;
}
void useTGHandle(struct TGHandle *tgHandle) {
SetConsoleActiveScreenBuffer(tgHandle->screenBufferHandle);
// Update the buffer sizes
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(tgHandle->screenBufferHandle, &info);
sizeTGDrawBuffer(&tgHandle->drawBuffer, info.dwSize.X, info.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
}
void updateTGHandle(struct TGHandle *tgHandle) {
COORD size = { tgHandle->drawBuffer.size.X, tgHandle->drawBuffer.size.Y }; // Buffer size
COORD pos = { 0, 0 }; // Start of the buffer coord
SMALL_RECT rect = {
.Left = 0,
.Top = 0,
.Right = size.X - 1,
.Bottom = size.Y - 1
}; // Rect to draw to on destination
WriteConsoleOutput(
tgHandle->screenBufferHandle,
tgHandle->drawBuffer.buffer,
size,
pos,
&rect
);
}
void setTGHandleCursorVisibility(struct TGHandle *tgHandle, int visible) {
// Copy the already-available cursor info
CONSOLE_CURSOR_INFO info;
GetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
// Modify the cursor visibility
info.bVisible = visible;
SetConsoleCursorInfo(tgHandle->screenBufferHandle, &info);
}
// You should be able to use a TGHandle's input buffer rather than creating your own
// for maximum memory conservation
int getTGInput(struct TGHandle *tgHandle, INPUT_RECORD *inputBuffer, int max) {
int availableRecords;
GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
int amountToRead = max;
if (availableRecords < max) {
amountToRead = availableRecords;
}
int numberRead;
ReadConsoleInput(
tgHandle->inputHandle,
inputBuffer,
amountToRead,
&numberRead
);
return numberRead;
}
// This function should be pretty performant if someone would not like to use
// the above function and mess around with buffers.
// Input record info: https://learn.microsoft.com/en-us/windows/console/input-record-str
int getTGNextInput(struct TGHandle *tgHandle, INPUT_RECORD *record) {
int availableRecords;
GetNumberOfConsoleInputEvents(tgHandle->inputHandle, &availableRecords);
if (availableRecords == 0) {
return 0;
}
ReadConsoleInput(
tgHandle->inputHandle,
tgHandle->inputBuffer,
1,
&availableRecords
);
*record = tgHandle->inputBuffer[0];
return 1;
}
int TGHandleResizeEvent(struct TGHandle *tgHandle, INPUT_RECORD record) {
if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
WINDOW_BUFFER_SIZE_RECORD size = record.Event.WindowBufferSizeEvent;
sizeTGDrawBuffer(&tgHandle->drawBuffer, size.dwSize.X, size.dwSize.Y);
clearTGDrawBuffer(&tgHandle->drawBuffer);
COORD bufferNewSize = {
size.dwSize.X,
size.dwSize.Y
};
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
}
}
test.c
#include "tg.h"
#include <time.h>
#include <stdio.h>
void getMessageAsStr(char *buf) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), 0,
buf, 256, NULL);
}
int main() {
// Error buffer
char buf[256];
// Create a drawing context to the screen
struct TGHandle context = TG();
useTGHandle(&context);
setTGHandleCursorVisibility(&context, 0); // Hide the cursor of course
struct TGDrawBuffer *buffer = &context.drawBuffer;
// Create a CHAR_INFO to draw with
CHAR_INFO info;
info.Attributes = BACKGROUND_BLUE | FOREGROUND_WHITE;
info.Char.AsciiChar = ' ';
info.Char.UnicodeChar = ' ';
INPUT_RECORD input;
const int STRING_BUF_SIZE = 64;
char *fpsCountBuffer = malloc(sizeof(char) * STRING_BUF_SIZE);
long start, end;
start = QueryPerformanceCounter(&start);
int running = 1;
while (running) {
// Start off with a nice clean slate
//clearTGDrawBuffer(buffer);
// Collect input to react to resize
while (getTGNextInput(&context, &input)) {
if (input.EventType == WINDOW_BUFFER_SIZE_EVENT) {
if (!TGHandleResizeEvent(&context, input)) {
OutputDebugString("Couldn't resize:\n");
getMessageAsStr(buf);
OutputDebugString(buf);
}
}
}
// Draw line along top and bottom
int i = 0;
while (i < buffer->size.X) {
TGDrawPixel(buffer, i, 0, info);
TGDrawPixel(buffer, i, buffer->size.Y - 1, info);
i++;
}
i = 0;
// Draw vertical lines
while (i < buffer->size.Y) {
TGDrawPixel(buffer, 0, i, info);
TGDrawPixel(buffer, buffer->size.X - 1, i, info);
i++;
}
// FPS count!
// Get time elapsed in millis
QueryPerformanceCounter(&end);
long fps = 1000000 / (end - start);
// Put it into the screen buffer
snprintf(fpsCountBuffer, STRING_BUF_SIZE, "Running at %ldhz, %dx%d", fps, buffer->size.X, buffer->size.Y);
TGDrawString(buffer, 1, 1, fpsCountBuffer, strlen(fpsCountBuffer));
start = end;
updateTGHandle(&context);
}
}
Spotted it right after I posted it. You can see that I'm taking the pointer location of the handle in TGHandleResizeEvent:
return SetConsoleScreenBufferSize(&tgHandle->screenBufferHandle, bufferNewSize);
This is, in fact, an invalid handle. Corrected code:
return SetConsoleScreenBufferSize(tgHandle->screenBufferHandle, bufferNewSize);
Related
I have to do a task for university in C. The program creates images in PGM format.The program compiles successfully, however, I can't run the program correctly. At the beginning of the main function the if operator needs 2 argc, but every time I try to run the program I just have one argc and I get the result
"Error: missing or bad parameters, usage a3 [imagefile]"
The code is:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
const double PI = 3.1415926535897932;
int** loadImage (const char *filename, int* width, int* height);
bool saveImage (const char *filename, int **pixels,
const unsigned int width, const unsigned int height);
/* *** task (a) *** */
int normalize(int value) {
return 0; // Dummy return
}
/* *** task (b) *** */
int** createImage(unsigned int width, unsigned int height, int value){
return NULL; // Dummy return
}
/* *** task (c) *** */
void freeImage(int** pixels, unsigned int width, unsigned int height){
}
/* *** task (d) *** */
int** inverter (int **pixels, int width, int height)
{
return NULL; // Dummy return
}
/* *** task (e) *** */
int** laplace (int **pixels, int width, int height)
{
return NULL; // Dummy return
}
/* *** task (f) *** */
int** rotate (int** pixels, int width, int height, double angle, int background)
{
return NULL; // Dummy return
}
int main (int argc, char **argv)
{
if (argc == 2)
{
int width=0, height=0;
int **image = loadImage (argv[1], &width, &height);
if (image != NULL)
{
int **image1 = inverter (image, width, height);
int **image2 = laplace (image, width, height);
int **image3 = rotate (image, width, height, 150, 128);
saveImage ("image0.pgm", image, width, height);
saveImage ("image1.pgm", image1, width, height);
saveImage ("image2.pgm", image2, width, height);
saveImage ("image3.pgm", image3, width, height);
freeImage (image, width, height);
freeImage (image1, width, height);
freeImage (image2, width, height);
freeImage (image3, width, height);
}
}
else {
printf ("Error: missing or bad parameters, usage a3 [imagefile]\n");
}
return 0;
}
/* *** Bild aus Datei laden *** */
int** loadImage (const char *filename, int* width, int* height)
{
const char *delimiter = " \n\r\t";
FILE *file = fopen (filename, "rt");
int** pixels;
bool isValid = true;
if (file != NULL)
{
char line [80];
int n = 0, params [3];
if (fgets (line, 80, file) == NULL) isValid = false;
if (strlen (line) != 3 || strncmp (line, "P2", 2)) isValid = false;
while (isValid && fgets (line, 80, file) != NULL)
{
if (strlen (line) <= 71)
{
if (line[0] == '#') continue;
char *p = strtok (line, delimiter);
while (isValid && p != NULL)
{
if (n < 3)
{
int a = atoi (p);
if (a <= 0) {isValid = false;}
else {params[n++] = a;}
}
else
{
if (n == 3)
{
if (params[2] != 255) {isValid = false;}
else {
pixels = createImage (params[0], params[1], 0);
*width = params[0];
*height = params[1];
}
}
if (n-3 < *width * *height)
{
int x = (n-3) % *width;
int y = (n-3) / *width;
int value = atoi (p);
pixels[y][x] = normalize(value);
n++;
}
else {isValid = false;}
}
p = strtok (NULL, delimiter);
}
}
else { isValid = false;}
}
fclose (file);
if (n-3 != *width * *height) {isValid = false;}
if (!isValid) freeImage (pixels, *width, *height);
}
return isValid ? pixels : NULL;
}
/* *** Bild in Datei speichern *** */
bool saveImage (const char *filename, int **pixels, unsigned int width, unsigned int height)
{
FILE *file = fopen (filename, "wt");
if (file != NULL && pixels != NULL)
{
fprintf (file, "P2\n");
fprintf (file, "%d %d\n", width, height);
fprintf (file, "%d\n", 255);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
fprintf (file, "%d\n", pixels[y][x]);
}
}
fclose (file);
return true;
}
return false;
}
The professor also said we should add following argument to the task in VSCode:
"args": [
"-g",
"-std=c99",
"-Wall",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-lm"
],
Your program a3.exe requires an argument which is the file name. To run it from command line, enter
.\a3.exe YourFilename
replacing YourFilename with the actual file name you want to use.
The following code is trying to count word frequency in a document, by using hashset and vector.
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/** ==================================== VECTOR ======================================= */
typedef enum {
true, false
} bool;
typedef int (*VectorCompareFunction)(const void *elemAddr1, const void *elemAddr2);
typedef void (*VectorFreeFunction)(void *elemAddr);
typedef struct {
int elemSize; //how many byte for each element
int elemNum; //number of current element in vector
int capacity; //maximum number of element vector can hold
void *elems; //pointer to data memory
VectorFreeFunction freefn; //pointer to the function used to free each element
} vector;
/**
* Reallocate a new memory of twice of original size
* return 1 if reallocation success, otherwise return -1.
*/
static void DoubleMemory(vector *v) {
void *tmp = realloc(v->elems, v->capacity * v->elemSize * 2);
assert(tmp != NULL);
v->elems = tmp;
v->capacity *= 2;
}
/**
* Constructor
*/
void VectorNew(vector *v, int elemSize, VectorFreeFunction freefn, int initialAllocation) {
v->elems = malloc(initialAllocation * elemSize);
assert(v->elems != NULL);
v->elemSize = elemSize;
v->elemNum = 0;
v->capacity = initialAllocation;
v->freefn = freefn;
}
/**
* Appends a new element to the end of the specified vector.
*/
void VectorAppend(vector *v, const void *elemAddr) {
/* double size if neccessary */
if (v->elemNum == v->capacity) DoubleMemory(v);
memcpy((char *)v->elems + v->elemNum * v->elemSize, elemAddr, v->elemSize);
v->elemNum++;
}
/**
* Search the specified vector for an element whose contents match the element passed as the key.
*/
int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchfn, int startIndex, bool isSorted) {
assert(key && searchfn);
if (v->elemNum == 0) return -1;
assert(startIndex >= 0 && startIndex < v->elemNum);
if (isSorted == true) {
/* binary search */
void *startAddr = (char *)v->elems + startIndex * v->elemSize;
int size = v->elemNum - startIndex;
void *resAddr = bsearch(key, startAddr, size, v->elemSize, searchfn);
return (resAddr != NULL)? ((char *)resAddr - (char *)v->elems) / v->elemSize : -1;
} else {
/* linear search */
for (int i = 0; i < v->elemNum; i++) {
if (searchfn((char *)v->elems + i * v->elemSize, key) == 0) {
return i;
}
}
return -1;
}
}
/**
* Overwrites the element at the specified position.
*/
void VectorReplace(vector *v, const void *elemAddr, int position) {
assert(position >= 0 && position < v->elemNum);
void *posAddr = (char *)v->elems + position * v->elemSize;
/* free the memory of old element first */
if (v->freefn != NULL) v->freefn(posAddr);
memcpy(posAddr, elemAddr, v->elemSize);
}
/** ==================================== HASHSET ======================================= */
typedef int (*HashSetHashFunction)(const void *elemAddr, int numBuckets);
typedef int (*HashSetCompareFunction)(const void *elemAddr1, const void *elemAddr2);
typedef void (*HashSetFreeFunction)(void *elemAddr);
typedef struct {
int elemNum; //current element number
int bucketNum; //number of hash bucket
int elemSize; //how many byte each element has
vector *buckets; //array of vector
HashSetHashFunction hashfn;
HashSetCompareFunction compfn;
HashSetFreeFunction freefn;
} hashset;
void HashSetNew(hashset *h, int elemSize, int numBuckets,
HashSetHashFunction hashfn, HashSetCompareFunction comparefn, HashSetFreeFunction freefn) {
assert(elemSize > 0 && numBuckets > 0 && hashfn != NULL && comparefn != NULL);
h->buckets = (vector *)malloc(numBuckets * sizeof(vector));
assert(h->buckets != NULL);
for (int i = 0; i < numBuckets; i++) {
vector *bucket = (vector *)((char *)h->buckets + i * sizeof(vector));
VectorNew(bucket, elemSize, freefn, 4);
}
h->bucketNum = numBuckets;
h->elemSize = elemSize;
h->elemNum = 0;
h->hashfn = hashfn;
h->compfn = comparefn;
h->freefn = freefn;
}
void HashSetEnter(hashset *h, const void *elemAddr) {
int hash = h->hashfn(elemAddr, h->bucketNum);
vector *bucket = (vector *)((char *)h->buckets + hash * sizeof(vector));
// search in the hash set first
int pos = VectorSearch(bucket, elemAddr, h->compfn, 0, false);
if (pos != -1) {
// replace the old one if find a match
VectorReplace(bucket, elemAddr, pos);
} else {
// otherwise insert the new one
VectorAppend(bucket, elemAddr);
h->elemNum++;
}
}
/** ==================================== DOC_FREQ & WORD_INDEX ======================================= */
/****************************************************************
*
* doc_freq is a key-value pair of [documentid, frequency]
* It's not supposed to be exposed to user or search engine.
* -----------------------------------------------------------
* It looks like:
* [1611742826915764000] [4 ]
* |-------------------| |-------|
* docid freq
***************************************************************/
typedef struct {
long docid;
int freq;
} doc_freq;
static void new_docfreq(doc_freq *df, long docid, int freq) {
df->docid = docid;
df->freq = freq;
}
/**
* HashSetHashFunction<doc_freq>
*/
static int hash_docfreq(const void *elemAddr, int numBuckets) {
doc_freq *df = (doc_freq *)elemAddr;
return (int)(df->docid % numBuckets);
}
/**
* HashSetCompareFunction<doc_freq>
*/
static int comp_docfreq(const void *elemAddr1, const void *elemAddr2) {
long id1 = ((doc_freq *)elemAddr1)->docid;
long id2 = ((doc_freq *)elemAddr2)->docid;
if (id1 < id2) {
return -1;
} else if (id1 > id2) {
return 1;
} else { // id1 == id2
return 0;
}
}
/**
* word_index is a index of a single word.
* ---------------------------------------
* A typical word_index looks like:
* [apple]: [doc1, 5], [doc3, 10], [doc5, 7]
* |-----| |------------------------------|
* word freqs
*/
typedef struct {
char *word;
hashset *freqs; // hashset<doc_freq>
} word_index;
static const size_t kWordIndexHashSetBuckets = 64;
static void new_wordindex(word_index *wi, const char *word) {
hashset h;
HashSetNew(&h, sizeof(doc_freq), kWordIndexHashSetBuckets, hash_docfreq, comp_docfreq, NULL);
wi->freqs = &h;
size_t wordlen = strlen(word);
wi->word = (char *)malloc(wordlen + 1); // +1 for null-termination
strcpy(wi->word, word);
(wi->word)[wordlen] = '\0';
}
/**
* Mainly used to build a word_index.
*/
void add_docfreq(word_index *wi, const long docid, const int frequency) {
doc_freq df;
new_docfreq(&df, docid, frequency);
HashSetEnter(wi->freqs, &df);
}
/** ==================================== UNIT-TEST ======================================= */
int main(void) {
/* apple: [1611742826915764000, 5][1611742826915538000, 10] */
word_index *apple = (word_index *)malloc(sizeof(word_index));
new_wordindex(apple, "apple");
add_docfreq(apple, 1611742826915764000L, 5);
add_docfreq(apple, 1611742826915538000L, 10);
}
It gave me a segmentation fault:
[1] 84309 segmentation fault testindexer
lldb find the problem occured when hashset try to callback the given pointer of function hashfn. I don't quite understand what is EXC_BAD_ACCESS (code=EXC_I386_GPFLT) here. I have done several unit test on hashset before, the HashSetEnter() function worked well with hashfn. Another unit test was conducted on hash_docfreq() function, it can also calculate correctly the hash number. I'm a little bit confused. Anyone can help? Thanks!
Process 89962 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000100003b83 testnothing`HashSetEnter(h=0x00007ffeefbff620, elemAddr=0x00007ffeefbff638) at test_nothing.c:130:13
127 }
128
129 void HashSetEnter(hashset *h, const void *elemAddr) {
-> 130 int hash = h->hashfn(elemAddr, h->bucketNum);
131 vector *bucket = (vector *)((char *)h->buckets + hash * sizeof(vector));
132 // search in the hash set first
133 int pos = VectorSearch(bucket, elemAddr, h->compfn, 0, false);
Target 0: (testnothing) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x0000000100003b83 testnothing`HashSetEnter(h=0x00007ffeefbff620, elemAddr=0x00007ffeefbff638) at test_nothing.c:130:13
frame #1: 0x0000000100003c37 testnothing`add_docfreq(wi=0x0000000100306060, docid=1611742826915764000, frequency=5) at test_nothing.c:222:2
frame #2: 0x0000000100003cae testnothing`main at test_nothing.c:235:2
frame #3: 0x00007fff70df0cc9 libdyld.dylib`start + 1
(lldb)
Running under gdb, after the fault, doing a tb command to get a stack traceback, we see:
#0 0x00000005004016e6 in ?? ()
#1 0x000000000040163a in HashSetEnter (h=0x7fffffffdc10,
elemAddr=0x7fffffffdc40) at orig.c:150
#2 0x0000000000401834 in add_docfreq (wi=0x405260, docid=1611742826915764000,
frequency=5) at orig.c:266
#3 0x0000000000401879 in main () at orig.c:278
(gdb) frame 1
#1 0x000000000040163a in HashSetEnter (h=0x7fffffffdc10,
elemAddr=0x7fffffffdc40) at orig.c:150
150 int hash = h->hashfn(elemAddr, h->bucketNum);
You are segfaulting in HashSetEnter, at the line:
int hash = h->hashfn(elemAddr, h->bucketNum);
This is because h is not valid at this point.
Examinining the source, the place that sets the value that is ultimately invalid, it is set in new_wordindex.
In new_wordindex, you are saving [and returning] the address of h.
h is a function scoped variable here, so it is no longer valid after the function returns.
You have to use malloc for this. And, later, you need to be able to free this pointer during cleanup.
Here's the refactored code for the incorrect function.
Note that to show old/original code vs. new/corrected code, I'm using preprocessor conditionals:
#if 0
// old/original code
// NOTE: this is _not_ compiled in
#else
// new/corrected code
// NOTE: this _is_ compiled in
#endif
The code under #if 0 can be elided/removed, leaving just the #else code.
static void
new_wordindex(word_index * wi, const char *word)
{
// NOTE/BUG: h is function scoped -- this can _not_ be saved and returned
// because it ceases to be valid when we return
#if 0
hashset h;
HashSetNew(&h, sizeof(doc_freq), kWordIndexHashSetBuckets, hash_docfreq, comp_docfreq, NULL);
wi->freqs = &h;
#else
hashset *h = malloc(sizeof(*h));
HashSetNew(h, sizeof(doc_freq), kWordIndexHashSetBuckets, hash_docfreq, comp_docfreq, NULL);
wi->freqs = h;
#endif
size_t wordlen = strlen(word);
wi->word = (char *) malloc(wordlen + 1); // +1 for null-termination
strcpy(wi->word, word);
(wi->word)[wordlen] = '\0';
}
I try (better tried) to implement a circular buffer with the following interface:
ring_buffer *ring_buffer_create(int capacity, int element_size);
void ring_buffer_destroy(ring_buffer *buffer)
const void *ring_buffer_read_acquire(ring_buffer *buffer, ring_buffer_loc *loc);
void ring_buffer_read_finish(ring_buffer *buffer, ring_buffer_loc loc);
void *ring_buffer_write_acquire(ring_buffer *buffer, ring_buffer_loc *loc);
void ring_buffer_write_finish(ring_buffer *buffer, ring_buffer_loc loc);
It should be possible to read / write multiple elements concurrently (and even in parallel). E.g.:
ring_buffer *buffer = ring_buffer_create(10, sizeof(int));
/* Write a single element */
ring_buffer_loc loc0;
int *i0 = ring_buffer_write_acquire(buffer, &loc);
*i0 = 42; // this could be a big data structure and way more expensive
ring_buffer_write_finish(buffer, loc0);
/* Write "concurrently" */
ring_buffer_loc loc1, loc2;
int *i1 = ring_buffer_write_acquire(buffer, &loc);
int *i2 = ring_buffer_write_acquire(buffer, &loc);
*i1 = 1729;
*i2 = 314;
ring_buffer_write_finish(buffer, loc1);
ring_buffer_write_finish(buffer, loc2);
All "acquire"-functions should be blocking until the operation is possible.
So far, so good. I thought this is simple and so I started with a clean implementation which is based on mutex. But soon I could see that this was far too slow for my use-case (100'000 writes and reads per second), so I switched over to spin-locks etc.
My implementation became quite messy and at some point (now), I started to think about why not something "simple" like this with the desired interface already exists? Probably, it is anyway not a great idea to re-implement something like this.
Maybe someone knows an implementation which has such an interface and which is blocking if the operation is not possible? I was looking quite long in the internet, but I could not find a good match for my problem. Maybe my desired interface is just "bad" or "wrong"?
Nevertheless, I add my current code. It basically assigns each "cell" (=value) a state which can be NONE (not set; the cell is basically empty), WRITING (someone acquired the cell to write data), READING (someone acquired the cell to read) and SET (the cell has a value which could be read). Each cell has a spin-lock which is used to update the cell state.
It then works like this:
When someone acquires a read and the current cell has the state "SET", then the value can be read (new state is READING) and the read index is increased. In all other cases a conditional variable is used to wait until an element is available. When an element read is finished, the cell state is changed to NONE and if any writers are waiting, a conditional variable signal is sent.
The same is true if a cell write is acquires. The only difference is that the cell needs the state "NONE" to be used and possible readers are signaled if there are any.
For some reasons the code sometimes locks and so I had to add a "dirty" timeout to my conditional variable. I would already be super happy if this could be solved, because the "timeout" basically makes the code polling (which is relatively ugly) and at the same time many context switches are done. Maybe someone sees the bug? The "new" code also has the disadvantage that it sometimes is really slow which is like a killer for my application. I attached the "old" and the "new" code (the changed lines are marked).
Thank you for helping me:)!
#include <stdio.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
typedef int ring_buffer_loc;
enum t_ring_buffer_cell_state
{
NONE = 0,
WRITING = 1,
READING = 2,
SET = 3
};
typedef struct {
char *buffer; // data
atomic_int_fast8_t *states; // state per cell
pthread_spinlock_t *locks; // lock per cell
int capacity;
int element_size;
pthread_spinlock_t read_i_lock;
int read_i;
pthread_spinlock_t write_i_lock;
int write_i;
pthread_spinlock_t waiting_readers_lock;
int waiting_readers;
pthread_spinlock_t waiting_writers_lock;
int waiting_writers;
pthread_mutex_t value_written_lock;
pthread_mutex_t value_read_lock;
pthread_cond_t value_written;
pthread_cond_t value_read;
} ring_buffer;
ring_buffer *ring_buffer_create(int capacity, int element_size)
{
ring_buffer *res = calloc(1, sizeof(ring_buffer));
res->buffer = calloc(capacity, element_size);
res->states = calloc(capacity, sizeof(*res->states));
res->locks = malloc(capacity * sizeof(*res->locks));
for (int i = 0; i < capacity; ++i) {
pthread_spin_init(&res->locks[i], PTHREAD_PROCESS_PRIVATE);
}
pthread_spin_init(&res->write_i_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->read_i_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->waiting_readers_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->waiting_writers_lock, PTHREAD_PROCESS_PRIVATE);
res->capacity = capacity;
res->element_size = element_size;
return res;
}
void ring_buffer_destroy(ring_buffer *buffer)
{
free(buffer->buffer);
free(buffer->states);
free(buffer);
}
static inline void ring_buffer_inc_index(ring_buffer *buffer, int *index)
{
*index = (*index + 1) % buffer->capacity;
}
void timespec_now_plus_ms(struct timespec *result, long ms_to_add)
{
const int one_second_us = 1000 * 1000 * 1000;
timespec_get(result, TIME_UTC);
const long nsec = result->tv_nsec + ms_to_add * 1000 * 1000;
result->tv_sec += nsec / one_second_us;
result->tv_nsec += nsec % one_second_us;
}
const void *ring_buffer_read_acquire(ring_buffer *buffer, ring_buffer_loc *loc)
{
bool is_waiting = false;
start:
pthread_spin_lock(&buffer->read_i_lock);
const int read_i = buffer->read_i;
pthread_spinlock_t *cell_lock = &buffer->locks[read_i];
pthread_spin_lock(cell_lock);
const int state = buffer->states[read_i];
if (state == NONE || state == WRITING || state == READING) {
if (!is_waiting) {
is_waiting = true;
pthread_spin_lock(&buffer->waiting_readers_lock);
++buffer->waiting_readers;
pthread_mutex_lock(&buffer->value_written_lock);
pthread_spin_unlock(&buffer->waiting_readers_lock);
} else {
pthread_mutex_lock(&buffer->value_written_lock);
}
pthread_spin_unlock(cell_lock);
pthread_spin_unlock(&buffer->read_i_lock);
// "new" code:
// struct timespec ts;
// do {
// timespec_now_plus_ms(&ts, 50);
// } while (pthread_cond_timedwait(&buffer->value_written, &buffer->value_written_lock, &ts) == ETIMEDOUT && buffer->states[read_i] == state);
// pthread_mutex_unlock(&buffer->value_written_lock);
// "old" code (which hangs quite often):
pthread_cond_wait(&buffer->value_written, &buffer->value_written_lock);
pthread_mutex_unlock(&buffer->value_written_lock);
goto start;
} else if (state == SET) {
if (is_waiting) {
pthread_spin_lock(&buffer->waiting_readers_lock);
--buffer->waiting_readers;
assert(buffer->waiting_readers >= 0);
pthread_spin_unlock(&buffer->waiting_readers_lock);
}
buffer->states[read_i] = READING;
ring_buffer_inc_index(buffer, &buffer->read_i);
pthread_spin_unlock(&buffer->read_i_lock);
pthread_spin_unlock(cell_lock);
*loc = read_i;
return &buffer->buffer[read_i * buffer->element_size];
} else {
printf("unknown state!\n");
exit(1);
}
}
void ring_buffer_read_finish(ring_buffer *buffer, ring_buffer_loc loc)
{
pthread_spinlock_t *cell_lock = &buffer->locks[loc];
pthread_spin_lock(cell_lock);
buffer->states[loc] = NONE;
pthread_spin_unlock(cell_lock);
pthread_spin_lock(&buffer->waiting_writers_lock);
if (buffer->waiting_writers > 0) {
pthread_cond_signal(&buffer->value_read);
}
pthread_spin_unlock(&buffer->waiting_writers_lock);
}
void *ring_buffer_write_acquire(ring_buffer *buffer, ring_buffer_loc *loc)
{
bool is_waiting = false;
start:
pthread_spin_lock(&buffer->write_i_lock);
const int write_i = buffer->write_i;
pthread_spinlock_t *cell_lock = &buffer->locks[write_i];
pthread_spin_lock(cell_lock);
const int state = buffer->states[write_i];
if (state == SET || state == READING || state == WRITING) {
if (!is_waiting) {
is_waiting = true;
pthread_spin_lock(&buffer->waiting_writers_lock);
++buffer->waiting_writers;
pthread_mutex_lock(&buffer->value_read_lock);
pthread_spin_unlock(&buffer->waiting_writers_lock);
} else {
pthread_mutex_lock(&buffer->value_read_lock);
}
pthread_spin_unlock(cell_lock);
pthread_spin_unlock(&buffer->write_i_lock);
// "new" code:
// struct timespec ts;
// do {
// timespec_now_plus_ms(&ts, 5);
// } while (pthread_cond_timedwait(&buffer->value_read, &buffer->value_read_lock, &ts) == ETIMEDOUT && buffer->states[write_i] == state);
// pthread_mutex_unlock(&buffer->value_read_lock);
// "old" code (which hangs quite often):
pthread_cond_wait(&buffer->value_read, &buffer->value_read_lock);
pthread_mutex_unlock(&buffer->value_read_lock);
goto start;
} else if (state == NONE) {
if (is_waiting) {
pthread_spin_lock(&buffer->waiting_writers_lock);
--buffer->waiting_writers;
assert(buffer->waiting_writers >= 0);
pthread_spin_unlock(&buffer->waiting_writers_lock);
}
buffer->states[write_i] = WRITING;
ring_buffer_inc_index(buffer, &buffer->write_i);
pthread_spin_unlock(&buffer->write_i_lock);
pthread_spin_unlock(cell_lock);
*loc = write_i;
return &buffer->buffer[write_i * buffer->element_size];
} else {
printf("unknown state!\n");
exit(1);
}
}
void ring_buffer_write_finish(ring_buffer *buffer, ring_buffer_loc loc)
{
pthread_spinlock_t *cell_lock = &buffer->locks[loc];
pthread_spin_lock(cell_lock);
buffer->states[loc] = SET;
pthread_spin_unlock(cell_lock);
pthread_spin_lock(&buffer->waiting_readers_lock);
if (buffer->waiting_readers > 0) {
pthread_cond_signal(&buffer->value_written);
}
pthread_spin_unlock(&buffer->waiting_readers_lock);
}
/* just for debugging */
void ring_buffer_dump(const ring_buffer *buffer)
{
printf("RingBuffer\n");
printf(" Capacity: %d\n", buffer->capacity);
printf(" Element size: %d\n", buffer->element_size);
printf(" Read index: %d\n", buffer->read_i);
printf(" Write index: %d\n", buffer->write_i);
printf(" Cells:\n");
for (int i = 0; i < buffer->capacity; ++i) {
printf(" [%d]: STATE = ", i);
switch (buffer->states[i]) {
case NONE:
printf("NONE");
break;
case WRITING:
printf("WRITING");
break;
case READING:
printf("READING");
break;
case SET:
printf("SET");
break;
}
printf("\n");
}
printf("\n");
}
/*
* Test run
*/
struct write_read_n_conf {
ring_buffer *buffer;
int n;
};
static void *producer_thread(void *arg)
{
struct write_read_n_conf conf = *(struct write_read_n_conf *)arg;
for (int i = 0; i < conf.n; ++i) {
ring_buffer_loc loc;
int *value = ring_buffer_write_acquire(conf.buffer, &loc);
*value = i;
ring_buffer_write_finish(conf.buffer, loc);
if (i % 1000 == 0) {
printf("%d / %d\n", i, conf.n);
}
}
return NULL;
}
static void *consumer_thread(void *arg)
{
struct write_read_n_conf conf = *(struct write_read_n_conf *)arg;
int tmp;
bool ok = true;
for (int i = 0; i < conf.n; ++i) {
ring_buffer_loc loc;
const int *value = ring_buffer_read_acquire(conf.buffer, &loc);
tmp = *value;
ring_buffer_read_finish(conf.buffer, loc);
ok = ok && (tmp == i);
}
printf("ok = %d\n", ok);
return (void *)ok;
}
void write_read_n_parallel(int n)
{
ring_buffer *buffer = ring_buffer_create(50, sizeof(int));
struct write_read_n_conf conf = {
.buffer = buffer,
.n = n
};
pthread_t consumer;
pthread_t producer;
pthread_create(&consumer, NULL, consumer_thread, &conf);
pthread_create(&producer, NULL, producer_thread, &conf);
pthread_join(producer, NULL);
void *res;
pthread_join(consumer, &res); // hacky way to pass a bool: res == NULL means false, and otherwise true
assert(res != NULL);
}
int main() {
write_read_n_parallel(10000000);
}
so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.
I've already been able to successfully implement length, get, set and copy functions for my faux string structs.
The struct:
struct text {
int capacity;
char *content;
};
typedef struct text text;
My function for creating new structs:
text *newText(char *s) {
printf("new Text from %s\n", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %d\n", sizeGot);
return out;
free(c);
}
My append function:
void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}
and finally the tests:
void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}
You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:
typedef struct {
int capacity;
char content[];
} text;
text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...
And obviously code such as this is nonsense:
return out;
free(c);
}
Enable compiler warnings and listen to them.
Och, some errors you have:
Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.
I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.
Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).
int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.
Try this:
size_t text_newsize(size_t sizeNeeded)
{
// I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot *= 2;
}
return sizeGot;
}
text *newText(char *s) {
printf("new Text from %s\n", s);
if (s == NULL) return NULL;
int sizeNeeded = strlen(s) + 1;
int sizeGot = text_newsize(sizeNeeded);
text *out = malloc(sizeof(*out));
if (out == NULL) {
return NULL;
}
out->content = malloc(sizeGot);
if (out->content == NULL) {
free(out);
return NULL;
}
strcpy(out->content, s);
out->capacity = sizeGot;
printf("the capacity is %d\n", sizeGot);
return out;
}
and this:
int append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
if (t1->capacity < sizeNeeded) {
// this could a text_resize(text*, size_t) function
int sizeGot = text_newsize(sizeNeeded);
void *tmp = realloc(t1->content, sizeGot);
if (tmp == NULL) return -ENOMEM;
t1->content = tmp;
t1->capacity = sizeGot;
}
strcat(t1->content, t2->content);
return 0;
}
Some remarks:
Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.
Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.
I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.
I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.
// text.h file
#ifndef TEXT_H_
#define TEXT_H_
#include <stddef.h>
#include <stdbool.h>
struct text;
typedef struct text text;
text *text_new(const char content[]);
void text_free(text *t);
int text_resize(text *t, size_t newsize);
int text_append(text *to, const text *from);
int text_append_mem(text *to, const void *from, size_t from_len);
const char *text_get(const text *t);
int text_append_str(text *to, const char *from);
char *text_get_nonconst(text *t);
size_t text_getCapacity(const text *t);
bool text_equal(const text *t1, const text *t2);
#endif // TEXT_H_
// text.c file
//#include "text.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
struct text {
size_t capacity;
char *content;
};
text *text_new(const char content[])
{
text * const t = malloc(sizeof(*t));
if (t == NULL) goto MALLOC_ERR;
const struct text zero = {
.capacity = 0,
.content = NULL,
};
*t = zero;
if (content != NULL) {
const int ret = text_append_str(t, content);
if (ret) {
goto TEXT_APPEND_ERR;
}
}
return t;
TEXT_APPEND_ERR:
free(t);
MALLOC_ERR:
return NULL;
}
void text_free(text *t)
{
assert(t != NULL);
free(t->content);
free(t);
}
int text_resize(text *t, size_t newcapacity)
{
// printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
// we resize in chunks
const size_t chunksize = 24;
// clap the capacity into multiple of 24
newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
void * const tmp = realloc(t->content, newcapacity);
if (tmp == NULL) return -ENOMEM;
t->content = tmp;
t->capacity = newcapacity;
return 0;
}
int text_append_mem(text *to, const void *from, size_t from_len)
{
if (to == NULL || from == NULL) return -EINVAL;
if (from_len == 0) return 0;
const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
const size_t newcapacity = oldcapacity + from_len + 1;
int ret = text_resize(to, newcapacity);
if (ret) return ret;
memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
to->content[newcapacity - 1] = '\0';
return 0;
}
int text_append_str(text *to, const char *from)
{
if (to == NULL || from == NULL) return -EINVAL;
return text_append_mem(to, from, strlen(from));
}
int text_append(text *to, const text *from)
{
if (to == NULL || from == NULL) return -EINVAL;
if (text_getCapacity(from) == 0) return 0;
return text_append_str(to, text_get(from));
}
const char *text_get(const text *t)
{
return t->content;
}
const size_t text_strlen(const text *t)
{
return t->capacity == 0 ? 0 : strlen(t->content);
}
size_t text_getCapacity(const text *t)
{
return t->capacity;
}
bool text_equal_str(const text *t, const char *str)
{
assert(t != NULL);
if (str == NULL && t->capacity == 0) return true;
const size_t strlength = strlen(str);
const size_t t_strlen = text_strlen(t);
if (t_strlen != strlength) return false;
if (memcmp(text_get(t), str, strlength) != 0) return false;
return true;
}
// main.c file
#include <stdio.h>
int text_testAppend(void) {
text *t = text_new("car");
if (t == NULL) return -1;
text *t2 = text_new("pet");
if (t2 == NULL) return -1;
if (text_append(t, t2)) return -1;
assert(text_equal_str(t, "carpet"));
assert(text_getCapacity(t) == 24);
text *t3 = text_new("789012345678901234");
if (t3 == NULL) return -1;
if (text_append(t, t3)) return -1;
assert(text_equal_str(t, "carpet789012345678901234"));
assert(text_getCapacity(t) == 48);
text_free(t);
text_free(t2);
text_free(t3);
return 0;
}
int main()
{
text *t1 = text_new("abc");
text_append_str(t1, "def");
printf("%s\n", text_get(t1));
text_free(t1);
printf("text_testAppend = %d\n", text_testAppend());
return 0;
}
I wanna show 3 .jpg pictures on LCD ,but the program crashed, it seems that the pointer did not init,here is the main() function:
#include <stdio.h>
#include <pthread.h>
int main(int argc,char **argv)
{
lcd_open("/dev/fb0");
int i = 0;
char* pic_path[3] = {"./jpeg/1.jpg","./jpeg/2.jpg","./jpeg/3.jpg"};
while(1)
{
if(i == 3)
{
i = 0;
}
lcd_draw_jpg(0, 0, pic_path[i], NULL, 0, 0);
pthread_testcancel();
sleep(1);
i++;
}
return 0;
}
It just show the 1st picture.
here is the display function Lcd.c
int lcd_draw_jpg(unsigned int x,unsigned int y,const char *pjpg_path,char *pjpg_buf,unsigned int jpg_buf_size,unsigned int jpg_half)
{
/*定义解码对象,错误处理对象*/
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
char *pcolor_buf = g_color_buf
char *pjpg
unsigned int i=0;
unsigned int color =0;
unsigned int count =0;
unsigned int x_s = x;
unsigned int x_e ;
unsigned int y_e ;
int jpg_fd;
unsigned int jpg_size;
unsigned int jpg_width;
unsigned int jpg_height;
//lcd_open("/dev/fb0");
if(pjpg_path!=NULL)
{
/* 申请jpg资源,权限可读可写 */
jpg_fd=open(pjpg_path,O_RDWR);
if(jpg_fd == -1)
{
printf("open %s error\n",pjpg_path);
return -1;
}
/* 获取jpg文件的大小 */
jpg_size=file_size_get(pjpg_path);
/* 为jpg文件申请内存空间 */
pjpg = malloc(jpg_size);
/* 读取jpg文件所有内容到内存 */
read(jpg_fd,pjpg,jpg_size);
}
else
{
jpg_size = jpg_buf_size;
pjpg = pjpg_buf;
}
/*注册出错处理*/
cinfo.err = jpeg_std_error(&jerr);
/*创建解码*/
jpeg_create_decompress(&cinfo);
/*直接解码内存数据*/
jpeg_mem_src(&cinfo,pjpg,jpg_size);
/*读文件头*/
jpeg_read_header(&cinfo, TRUE);
/*开始解码*/
jpeg_start_decompress(&cinfo);
if(jpg_half)
{
x_e = x_s+(cinfo.output_width/2);
y_e = y +(cinfo.output_height/2);
/*读解码数据*/
while(cinfo.output_scanline < cinfo.output_height)
{
pcolor_buf = g_color_buf;
/* 读取jpg一行的rgb值 */
jpeg_read_scanlines(&cinfo,(JSAMPARRAY)&pcolor_buf,1);
/* 再读取jpg一行的rgb值 */
jpeg_read_scanlines(&cinfo,(JSAMPARRAY)&pcolor_buf,1);
for(i=0; i<(cinfo.output_width/2); i++)
{
/* 获取rgb值 */
color = *(pcolor_buf+2);
color = color | *(pcolor_buf+1)<<8;
color = color | *(pcolor_buf)<<16;
/* 显示像素点 */
lcd_draw_point(x,y,color);
pcolor_buf +=6;
x++;
}
/* 换行 */
y++;
x = x_s;
}
}
else
{
x_e = x_s+cinfo.output_width;
y_e = y +cinfo.output_height;
/*读解码数据*/
while(cinfo.output_scanline < cinfo.output_height )
{
pcolor_buf = g_color_buf;
/* read one line's of jpg */
jpeg_read_scanlines(&cinfo,(JSAMPARRAY)&pcolor_buf,1);
for(i=0; i<cinfo.output_width; i++)
{
/* get rgb value */
color = *(pcolor_buf+2);
color = color | *(pcolor_buf+1)<<8;
color = color | *(pcolor_buf)<<16;
/* display pixel point */
lcd_draw_point(x,y,color);
pcolor_buf +=3;
x++;
}
/* 换行 */
y++;
x = x_s;
}
}
/*解码完成*/
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
if(pjpg_path!=NULL)
{
/* 关闭jpg文件 */
close(jpg_fd);
/* 释放jpg文件内存空间 */
free(pjpg);
}
close_lcd();
return 0;
}
this looks like Memory Leak or something else......
these seems a little weird,and