Skip to content

Commit fe51d58

Browse files
committed
Clean file reading and lexer.
1 parent a1e6831 commit fe51d58

1 file changed

Lines changed: 88 additions & 72 deletions

File tree

objzero.c

Lines changed: 88 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -169,41 +169,23 @@ typedef struct {
169169
uint32_t line, column;
170170
} Token;
171171

172-
static void initLexer(Lexer *_lexer, char *_buf) {
173-
_lexer->buf = _buf;
174-
_lexer->line = _lexer->column = 1;
172+
static void initLexer(Lexer *_lexer) {
173+
_lexer->buf = NULL;
174+
_lexer->column = 1;
175+
_lexer->line = 0;
175176
}
176177

177178
static bool isEol(const Lexer *_lexer) {
178-
return (_lexer->buf[0] == '\n' || (_lexer->buf[0] == '\r' && _lexer->buf[1] != '\n'));
179-
}
180-
181-
static bool isEof(const Lexer *_lexer) {
182179
return (_lexer->buf[0] == 0);
183180
}
184181

185182
static bool isWhitespace(const Lexer *_lexer) {
186183
return (_lexer->buf[0] == ' ' || _lexer->buf[0] == '\t' || _lexer->buf[0] == '\r');
187184
}
188185

189-
static void skipLine(Lexer *_lexer) {
190-
for (;;) {
191-
if (isEof(_lexer))
192-
break;
193-
if (isEol(_lexer)) {
194-
_lexer->column = 1;
195-
_lexer->line++;
196-
_lexer->buf++;
197-
break;
198-
}
199-
_lexer->buf++;
200-
_lexer->column++;
201-
}
202-
}
203-
204186
static void skipWhitespace(Lexer *_lexer) {
205187
for (;;) {
206-
if (isEof(_lexer))
188+
if (isEol(_lexer))
207189
break;
208190
if (!isWhitespace(_lexer))
209191
break;
@@ -212,13 +194,19 @@ static void skipWhitespace(Lexer *_lexer) {
212194
}
213195
}
214196

197+
static void lexerSetLine(Lexer *_lexer, char *_buf) {
198+
_lexer->column = 1;
199+
_lexer->line++;
200+
_lexer->buf = _buf;
201+
}
202+
215203
static void tokenize(Lexer *_lexer, Token *_token, bool includeWhitespace) {
216204
uint32_t i = 0;
217205
skipWhitespace(_lexer);
218206
_token->line = _lexer->line;
219207
_token->column = _lexer->column;
220208
for (;;) {
221-
if (isEof(_lexer) || isEol(_lexer) || (!includeWhitespace && isWhitespace(_lexer)))
209+
if (isEol(_lexer) || (!includeWhitespace && isWhitespace(_lexer)))
222210
break;
223211
_token->text[i++] = _lexer->buf[0];
224212
_lexer->buf++;
@@ -251,29 +239,60 @@ static bool parseInt(Lexer *_lexer, int32_t *_result) {
251239
return true;
252240
}
253241

254-
static char *readFile(const char *_filename, size_t *_length) {
255-
FILE *f;
256-
OBJZ_FOPEN(f, _filename, "rb");
257-
if (!f)
258-
return NULL;
259-
fseek(f, 0, SEEK_END);
260-
const size_t length = (size_t)ftell(f);
261-
if (_length)
262-
*_length = length;
263-
fseek(f, 0, SEEK_SET);
264-
if (length <= 0) {
265-
fclose(f);
266-
return NULL;
242+
typedef struct {
243+
char *buffer;
244+
size_t length;
245+
size_t pos;
246+
} File;
247+
248+
bool fileOpen(File *_file, const char *_filename) {
249+
FILE *handle;
250+
OBJZ_FOPEN(handle, _filename, "rb");
251+
if (!handle)
252+
return false;
253+
fseek(handle, 0, SEEK_END);
254+
_file->length = (size_t)ftell(handle);
255+
fseek(handle, 0, SEEK_SET);
256+
if (_file->length == 0) {
257+
fclose(handle);
258+
return false;
267259
}
268-
char *buffer = objz_malloc(length + 1);
269-
const size_t bytesRead = fread(buffer, 1, length, f);
270-
fclose(f);
271-
if (bytesRead < length) {
272-
objz_free(buffer);
273-
return NULL;
260+
_file->pos = 0;
261+
_file->buffer = objz_malloc(_file->length + 1);
262+
const size_t bytesRead = fread(_file->buffer, 1, _file->length, handle);
263+
fclose(handle);
264+
if (bytesRead < _file->length) {
265+
objz_free(_file->buffer);
266+
return false;
267+
}
268+
_file->buffer[_file->length] = 0;
269+
return true;
270+
}
271+
272+
void fileClose(File *_file) {
273+
objz_free(_file->buffer);
274+
}
275+
276+
char *fileReadLine(File *_file) {
277+
if (_file->buffer[_file->pos] == 0)
278+
return NULL; // eof
279+
char *start = &_file->buffer[_file->pos];
280+
// Find eol. Newline is replaced with a null terminator. Position is set to the start of the next line.
281+
for (;;) {
282+
char *c = &_file->buffer[_file->pos];
283+
if (*c == 0) {
284+
break;
285+
}
286+
if (*c == '\r')
287+
*c = 0; // Null terminate here, but keep reading until newline.
288+
else if (*c == '\n') {
289+
*c = 0;
290+
_file->pos++;
291+
break;
292+
}
293+
_file->pos++;
274294
}
275-
buffer[length] = 0;
276-
return buffer;
295+
return start;
277296
}
278297

279298
#define OBJZ_MAT_TOKEN_STRING 0
@@ -320,20 +339,21 @@ static bool loadMaterialFile(const char *_objFilename, const char *_materialName
320339
} else
321340
OBJZ_STRNCPY(filename, sizeof(filename), _materialName);
322341
bool result = false;
323-
char *buffer = readFile(filename, NULL);
324-
if (!buffer)
342+
File file;
343+
if (!fileOpen(&file, filename))
325344
return result;
326345
Lexer lexer;
327-
initLexer(&lexer, buffer);
346+
initLexer(&lexer);
328347
Token token;
329348
objzMaterial mat;
330349
materialInit(&mat);
331350
for (;;) {
351+
char *line = fileReadLine(&file);
352+
if (!line)
353+
break;
354+
lexerSetLine(&lexer, line);
332355
tokenize(&lexer, &token, false);
333-
if (token.text[0] == 0) {
334-
if (isEof(&lexer))
335-
break;
336-
} else if (OBJZ_STRICMP(token.text, "newmtl") == 0) {
356+
if (OBJZ_STRICMP(token.text, "newmtl") == 0) {
337357
tokenize(&lexer, &token, false);
338358
if (token.text[0] == 0) {
339359
snprintf(s_error, OBJZ_MAX_ERROR_LENGTH, "(%u:%u) Expected name after 'newmtl'", token.line, token.column);
@@ -366,13 +386,12 @@ static bool loadMaterialFile(const char *_objFilename, const char *_materialName
366386
}
367387
}
368388
}
369-
skipLine(&lexer);
370389
}
371390
if (mat.name[0] != 0)
372391
arrayAppend(_materials, &mat);
373392
result = true;
374393
cleanup:
375-
objz_free(buffer);
394+
fileClose(&file);
376395
return result;
377396
}
378397

@@ -710,35 +729,34 @@ void objz_setVertexFormat(size_t _stride, size_t _positionOffset, size_t _texcoo
710729
}
711730

712731
objzModel *objz_load(const char *_filename) {
713-
objzModel *model = NULL;
714-
size_t fileLength;
715-
char *buffer = readFile(_filename, &fileLength);
716-
if (!buffer) {
732+
File file;
733+
if (!fileOpen(&file, _filename)) {
717734
snprintf(s_error, OBJZ_MAX_ERROR_LENGTH, "Failed to read file '%s'", _filename);
718735
return NULL;
719736
}
720737
// Parse the obj file and any material files.
721738
Array materials, positions, texcoords, normals, tempObjects, tempFaces;
722739
Array faceIndices, tempFaceIndices; // Re-used per face.
723740
arrayInit(&materials, sizeof(objzMaterial), 16);
724-
arrayInit(&positions, sizeof(float) * 3, guessArrayInitialSize(fileLength, UINT16_MAX, 1<<21));
725-
arrayInit(&texcoords, sizeof(float) * 2, guessArrayInitialSize(fileLength, UINT16_MAX, UINT16_MAX));
726-
arrayInit(&normals, sizeof(float) * 3, guessArrayInitialSize(fileLength, 1<<14, 1<<14));
727-
arrayInit(&tempObjects, sizeof(TempObject), guessArrayInitialSize(fileLength, 64, 64));
728-
arrayInit(&tempFaces, sizeof(TempFace), guessArrayInitialSize(fileLength, 1<<17, 1<<23));
741+
arrayInit(&positions, sizeof(float) * 3, guessArrayInitialSize(file.length, UINT16_MAX, 1<<21));
742+
arrayInit(&texcoords, sizeof(float) * 2, guessArrayInitialSize(file.length, UINT16_MAX, UINT16_MAX));
743+
arrayInit(&normals, sizeof(float) * 3, guessArrayInitialSize(file.length, 1<<14, 1<<14));
744+
arrayInit(&tempObjects, sizeof(TempObject), guessArrayInitialSize(file.length, 64, 64));
745+
arrayInit(&tempFaces, sizeof(TempFace), guessArrayInitialSize(file.length, 1<<17, 1<<23));
729746
arrayInit(&faceIndices, sizeof(IndexTriplet), 8);
730747
arrayInit(&tempFaceIndices, sizeof(IndexTriplet), 8);
731748
int32_t currentMaterialIndex = -1;
732749
uint32_t flags = 0;
733750
Lexer lexer;
734-
initLexer(&lexer, buffer);
751+
initLexer(&lexer);
735752
Token token;
736753
for (;;) {
754+
char *line = fileReadLine(&file);
755+
if (!line)
756+
break;
757+
lexerSetLine(&lexer, line);
737758
tokenize(&lexer, &token, false);
738-
if (token.text[0] == 0) {
739-
if (isEof(&lexer))
740-
break;
741-
} else if (OBJZ_STRICMP(token.text, "f") == 0) {
759+
if (OBJZ_STRICMP(token.text, "f") == 0) {
742760
// Get current object.
743761
if (tempObjects.length == 0) {
744762
// No objects specifed, but there's a face, so create one.
@@ -840,7 +858,6 @@ objzModel *objz_load(const char *_filename) {
840858
arrayAppend(&texcoords, texcoord);
841859
flags |= OBJZ_FLAG_TEXCOORDS;
842860
}
843-
skipLine(&lexer);
844861
}
845862
// Do some post-processing of parsed data:
846863
// * find unique vertices from separately index vertex attributes (pos, texcoord, normal).
@@ -904,7 +921,7 @@ objzModel *objz_load(const char *_filename) {
904921
arrayAppend(&objects, &object);
905922
}
906923
// Build output data structure.
907-
model = objz_malloc(sizeof(objzModel));
924+
objzModel *model = objz_malloc(sizeof(objzModel));
908925
model->flags = flags;
909926
if (s_indexFormat == OBJZ_INDEX_FORMAT_U32 || (flags & OBJZ_FLAG_INDEX32))
910927
model->indices = indices.data;
@@ -951,16 +968,15 @@ objzModel *objz_load(const char *_filename) {
951968
objz_free(tempFaceIndices.data);
952969
objz_free(vertexHashMap.indices.data);
953970
vertexHashMapDestroy(&vertexHashMap);
954-
objz_free(buffer);
955971
return model;
956972
error:
973+
fileClose(&file);
957974
objz_free(materials.data);
958975
objz_free(positions.data);
959976
objz_free(texcoords.data);
960977
objz_free(normals.data);
961978
objz_free(faceIndices.data);
962979
objz_free(tempFaceIndices.data);
963-
objz_free(buffer);
964980
return NULL;
965981
}
966982

0 commit comments

Comments
 (0)