Version 7.0
Copyright © 2021 Lowell D. Thomas
APG
… an ABNF Parser Generator
lines.c
Go to the documentation of this file.
1 /* *************************************************************************************
2  Copyright (c) 2021, Lowell D. Thomas
3  All rights reserved.
4 
5  This file is part of APG Version 7.0.
6  APG Version 7.0 may be used under the terms of the BSD 2-Clause License.
7 
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions are met:
10 
11  1. Redistributions of source code must retain the above copyright notice, this
12  list of conditions and the following disclaimer.
13 
14  2. Redistributions in binary form must reproduce the above copyright notice,
15  this list of conditions and the following disclaimer in the documentation
16  and/or other materials provided with the distribution.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 * *************************************************************************************/
44 #include "../library/lib.h"
45 #include "./lines.h"
46 
48 #define LF 10
50 #define CR 13
51 
53 static const void* s_vpMagicNumber = (void*)"lines";
54 
58 typedef struct{
59  const void* vpValidate;
61  void* vpMem;
63  char* cpInput;
65  void* vpVecLines;
69 } lines;
70 
71 static void vInputLines(lines* spCtx);
72 static abool bFindLine(line* spLines, aint uiLineCount, aint uiCharIndex, aint* uipLine);
73 
85 void* vpLinesCtor(exception* spEx, const char* cpInput, aint uiLength){
86  if(bExValidate(spEx)){
87  if(!cpInput || !uiLength){
88  XTHROW(spEx, "input is NULL or empty");
89  }
90  // get the context
91  void* vpMem = vpMemCtor(spEx);
92  lines* spCtx = (lines*)vpMemAlloc(vpMem, sizeof(lines));
93  memset((void*)spCtx, 0, sizeof(lines));
94 
95  // keep a local copy of the characters
96  spCtx->cpInput = (char*)vpMemAlloc(vpMem, ((sizeof(char) * uiLength)));
97  memcpy((void*)spCtx->cpInput, (void*)cpInput, (sizeof(char) * uiLength));
98  spCtx->uiLength = uiLength;
99 
100  // set up the context
101  spCtx->vpMem = vpMem;
102  spCtx->spException = spEx;
103  spCtx->vpVecLines = vpVecCtor(vpMem, sizeof(line), 512);
104 
105  // compute lines
106  vInputLines(spCtx);
107  spCtx->vpValidate = s_vpMagicNumber;
108  return (void*)spCtx;
109  }else{
110  vExContext();
111  }
112  return NULL;
113 }
114 
123 void vLinesDtor(void* vpCtx){
124  lines* spCtx = (lines*)vpCtx;
125  if(vpCtx){
126  if(spCtx->vpValidate == s_vpMagicNumber){
127  void* vpMem = spCtx->vpMem;
128  memset(vpCtx, 0, sizeof(lines));
129  vMemDtor(vpMem);
130  }else{
131  vExContext();
132  }
133  }
134 }
135 
146 abool bLinesFindLine(void* vpCtx, aint uiOffset, aint* uipLine, aint* uipRelOffset){
147  abool bReturn = APG_FAILURE;
148  lines* spCtx = (lines*)vpCtx;
149  if(vpCtx && (spCtx->vpValidate == s_vpMagicNumber)){
150  if(!uipLine || !uipRelOffset){
151  XTHROW(spCtx->spException, "line and relative offset pointers cannot be NULL");
152  }
153  if(bFindLine(spCtx->spLines, spCtx->uiLineCount, uiOffset, uipLine)){
154  *uipRelOffset = uiOffset - spCtx->spLines[*uipLine].uiCharIndex;
155  bReturn = APG_SUCCESS;
156  }
157  }else{
158  vExContext();
159  }
160  return bReturn;
161 }
162 
170 line* spLinesFirst(void* vpCtx){
171  lines* spCtx = (lines*)vpCtx;
172  if(vpCtx && (spCtx->vpValidate == s_vpMagicNumber)){
173  spCtx->uiIterator = 1;
174  return spCtx->spLines;
175  }
176  vExContext();
177  return NULL;
178 }
185 line* spLinesNext(void* vpCtx){
186  lines* spCtx = (lines*)vpCtx;
187  if(vpCtx && (spCtx->vpValidate == s_vpMagicNumber)){
188  if(spCtx->uiIterator < spCtx->uiLineCount){
189  return &spCtx->spLines[spCtx->uiIterator++];
190  }
191  return NULL;
192  }
193  vExContext();
194  return NULL;
195 }
196 
202 aint uiLinesCount(void* vpCtx){
203  lines* spCtx = (lines*)vpCtx;
204  if(vpCtx && (spCtx->vpValidate == s_vpMagicNumber)){
205  return spCtx->uiLineCount;
206  }
207  vExContext();
208  return 0;
209 }
210 
218 aint uiLinesLength(void* vpCtx){
219  lines* spCtx = (lines*)vpCtx;
220  if(vpCtx && (spCtx->vpValidate == s_vpMagicNumber)){
221  return spCtx->uiLength;
222  }
223  vExContext();
224  return 0;
225 }
226 
227 static abool bFindLine(line* spLines, aint uiLineCount, aint uiCharIndex, aint* uipLine) {
228  abool bReturn = APG_FAILURE;
229  if (!spLines || !uiLineCount) {
230  goto fail;
231  }
232  aint ui;
233  line* spThis;
234  if (uiLineCount < 5) {
235  // linear search
236  for (ui = 0; ui < uiLineCount; ui += 1) {
237  spThis = &spLines[ui];
238  if ((uiCharIndex >= spThis->uiCharIndex) && (uiCharIndex < (spThis->uiCharIndex + spThis->uiLineLength))) {
239  *uipLine = ui;
240  goto success;
241  }
242  }
243  goto fail;
244  } else {
245  // binary search (https://en.wikipedia.org/wiki/Binary_search_algorithm)
246  aint uiL = 0;
247  aint uiR = uiLineCount - 1;
248  aint uiM;
249  while (uiL < uiR) {
250  uiM = uiL + (uiR - uiL) / 2;
251  spThis = &spLines[uiM];
252  if (uiCharIndex >= (spThis->uiCharIndex + spThis->uiLineLength)) {
253  uiL = uiM + 1;
254  continue;
255  }
256  if (uiCharIndex < spThis->uiCharIndex) {
257  uiR = uiM - 1;
258  continue;
259  }
260  *uipLine = uiM;
261  goto success;
262  }
263  if (uiL == uiR) {
264  spThis = &spLines[uiL];
265  if ((uiCharIndex >= spThis->uiCharIndex) && (uiCharIndex < (spThis->uiCharIndex + spThis->uiLineLength))) {
266  *uipLine = uiL;
267  goto success;
268  }
269  }
270  goto fail;
271  }
272  success: bReturn = APG_SUCCESS;
273  fail: ;
274  return bReturn;
275 }
276 
281 static void vInputLines(lines* spCtx) {
282  aint uiLineIndex = 0;
283  aint uiCharIndex = 0;
284  aint uiTextLength = 0;
285  char cChar;
286  line sLine;
287  void* vpVec = spCtx->vpVecLines;
288  const char* cpInput = spCtx->cpInput;
289  aint uiLen = spCtx->uiLength;
290  vVecClear(vpVec);
291  while (uiCharIndex < uiLen) {
292  cChar = cpInput[uiCharIndex];
293  if (cChar == LF) {
294  // LF line ending
295  sLine.uiLineIndex = uiLineIndex;
296  sLine.uiCharIndex = uiCharIndex - uiTextLength;
297  sLine.uiTextLength = uiTextLength;
298  sLine.uiLineLength = uiTextLength + 1;
299  uiCharIndex += 1;
300  sLine.caLineEnd[0] = LF;
301  sLine.caLineEnd[1] = 0;
302  sLine.caLineEnd[2] = 0;
303  vpVecPush(vpVec, (void*) &sLine);
304  uiLineIndex += 1;
305  uiTextLength = 0;
306  } else if (cChar == CR) {
307  sLine.uiLineIndex = uiLineIndex;
308  sLine.uiCharIndex = uiCharIndex - uiTextLength;
309  sLine.uiTextLength = uiTextLength;
310  if ((uiCharIndex < (uiLen - 1)) && (cpInput[uiCharIndex + 1] == LF)) {
311  // CRLF line ending
312  sLine.uiLineLength = uiTextLength + 2;
313  sLine.caLineEnd[0] = CR;
314  sLine.caLineEnd[1] = LF;
315  sLine.caLineEnd[2] = 0;
316  uiCharIndex += 2;
317  } else {
318  // CR line ending
319  sLine.uiLineLength = uiTextLength + 1;
320  sLine.caLineEnd[0] = CR;
321  sLine.caLineEnd[1] = 0;
322  sLine.caLineEnd[2] = 0;
323  uiCharIndex += 1;
324  }
325  vpVecPush(vpVec, (void*) &sLine);
326  uiLineIndex += 1;
327  uiTextLength = 0;
328  } else {
329  uiTextLength += 1;
330  uiCharIndex += 1;
331  }
332  }
333  if (uiTextLength > 0) {
334  // last line had no line ending
335  sLine.uiLineIndex = uiLineIndex;
336  sLine.uiCharIndex = uiCharIndex - uiTextLength;
337  sLine.uiTextLength = uiTextLength;
338  sLine.uiLineLength = uiTextLength;
339  sLine.caLineEnd[0] = 0;
340  sLine.caLineEnd[1] = 0;
341  sLine.caLineEnd[2] = 0;
342  vpVecPush(vpVec, (void*) &sLine);
343  }
344  spCtx->spLines = (line*) vpVecFirst(spCtx->vpVecLines);
345  spCtx->uiLineCount = uiVecLen(spCtx->vpVecLines);
346 }
line::uiLineLength
aint uiLineLength
The number of characters in the line, including the line end characters.
Definition: lines.h:43
lines.h
Header file for the lines object.
APG_FAILURE
#define APG_FAILURE
Definition: apg.h:308
vpLinesCtor
void * vpLinesCtor(exception *spEx, const char *cpInput, aint uiLength)
The lines object constructor.
Definition: lines.c:85
vMemDtor
void vMemDtor(void *vpCtx)
Destroys a Memory component. Frees all memory allocated.
Definition: memory.c:141
fmt_tag::vpMem
void * vpMem
Pointer to a memory object for allocating all memory associated with this object.
Definition: format.c:81
uiLinesLength
aint uiLinesLength(void *vpCtx)
Returns the number of text characters.
Definition: lines.c:218
vExContext
void vExContext()
Handles bad context pointers.
Definition: exception.c:126
bLinesFindLine
abool bLinesFindLine(void *vpCtx, aint uiOffset, aint *uipLine, aint *uipRelOffset)
Find the line that the given character is in.
Definition: lines.c:146
lines::vpValidate
const void * vpValidate
A "magic number" to validate the context.
Definition: lines.c:59
XTHROW
#define XTHROW(ctx, msg)
Exception throw macro.
Definition: exception.h:67
lines
The lines object context.
Definition: lines.c:58
LF
#define LF
Definition: lines.c:49
aint
uint_fast32_t aint
The APG parser's unsigned integer type.
Definition: apg.h:79
lines::cpInput
char * cpInput
Pointer to the character array.
Definition: lines.c:63
vpMemAlloc
void * vpMemAlloc(void *vpCtx, aint uiBytes)
Allocates memory.
Definition: memory.c:196
uiVecLen
aint uiVecLen(void *vpCtx)
Get the vector length. That is, the number of elements on the vector.
Definition: vector.c:385
vpVecCtor
void * vpVecCtor(void *vpMem, aint uiElementSize, aint uiInitialAlloc)
The vector object constructor.
Definition: vector.c:118
lines::spLines
line * spLines
Pointer to the first line.
Definition: lines.c:66
CR
#define CR
Definition: lines.c:50
APG_SUCCESS
#define APG_SUCCESS
Definition: apg.h:307
exception
A structure to describe the type and location of a caught exception.
Definition: exception.h:47
lines::uiIterator
aint uiIterator
Used by the iterator.
Definition: lines.c:68
line::uiLineIndex
aint uiLineIndex
The zero-based line index.
Definition: lines.h:41
bExValidate
abool bExValidate(exception *spException)
Test an exception structure for validity.
Definition: exception.c:70
lines::uiLineCount
aint uiLineCount
Number of lines in the array.
Definition: lines.c:67
line::uiTextLength
aint uiTextLength
The number of characters in the line, excluding the line end characters.
Definition: lines.h:44
vpVecFirst
void * vpVecFirst(void *vpCtx)
Get the first element one the vector. The vector is not altered.
Definition: vector.c:326
line
Defines the characteristics of a single line.
Definition: lines.h:40
vpMemCtor
void * vpMemCtor(exception *spException)
Construct a memory component.
Definition: memory.c:121
spLinesNext
line * spLinesNext(void *vpCtx)
Returns the next line of text from the iterator.
Definition: lines.c:185
lines::uiLength
aint uiLength
Number of characters in the array.
Definition: lines.c:64
lines::spException
exception * spException
Pointer to an exception structure to report fatal errors back to the application's catch block.
Definition: lines.c:60
spLinesFirst
line * spLinesFirst(void *vpCtx)
Initialize an iterator over the lines.
Definition: lines.c:170
lines::vpMem
void * vpMem
Pointer to a memory object for allocating all memory associated with this object.
Definition: lines.c:62
vLinesDtor
void vLinesDtor(void *vpCtx)
The lines object destructor.
Definition: lines.c:123
abool
uint8_t abool
abool is the APG bool type.
Definition: apg.h:140
lines::vpVecLines
void * vpVecLines
Pointer to a vector of parsed lines.
Definition: lines.c:65
line::caLineEnd
char caLineEnd[3]
The actual, null-terminated string of line ending character(s), if any.
Definition: lines.h:45
vpVecPush
void * vpVecPush(void *vpCtx, void *vpElement)
Adds one element to the end of the array.
Definition: vector.c:193
vVecClear
void vVecClear(void *vpCtx)
Clears all used elements in a vector component.
Definition: vector.c:420
line::uiCharIndex
aint uiCharIndex
The zero-based index of the first character of the line.
Definition: lines.h:42
uiLinesCount
aint uiLinesCount(void *vpCtx)
Returns the number of lines of text.
Definition: lines.c:202
APG Version 7.0 is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.