-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #57 - Recover responsiveness to stdin and upgrade strOrFile #140
base: master
Are you sure you want to change the base?
Issue #57 - Recover responsiveness to stdin and upgrade strOrFile #140
Conversation
…etBuf, and consuming this content with existing functions
…xample is as expected in theGraph
…if '-' appears in any other context
…LEDA format) to Adj List
…r lists and function calls.
…, which is now called in gp_Read() with the inputContainer strOrFileP (instead of original call to _ReadGraphFromG6FilePointer())
…sful. Additionally, manual tests redirecting input file contents to planarity executable with stdin as "filename" works.
…graph transformation to .g6 of nauty_example.g6.0-based.AdjList.out.txt
…s already NULL when we tried to take the string after _WriteGraph() in gp_WriteToString() with Mode being WRITE_G6, which had already been taken and assigned in _WriteGraphToG6StrOrFile()
…pChar() so that they return OK/NOTOK, and so that they also check that theStrOrFile has containerType of INPUT_CONTAINER, as well as added checks to sf_getc(), sf_ReadSingleDigit(), sf_ReadInteger(), sf_ungetc(), sf_ungets(), and sf_fgets() that they are operating over INPUT_CONTAINER and that sf_fputs() is operating over an OUTPUT_CONTAINER.
…an void * requiring extraDataSize to ensure we don't overrun allocated memory). The only instance is DrawPlanar; writing extra data was tested using planarity -s -d nauty_example.g6 nauty_example.g6.s.d.WriteExtraData.out.txt and reading the extra data was tested using planarity -s -d nauty_example.g6.s.d.WriteExtraData.out.txt nauty_example.g6.s.d.ReadExtraData.out.txt
…dereference and assign the string taken from the output container
…t a non-NULL theStr was provided. This is because we don't want to have an incongruety between what would happen if the container had a file vs. a string. We *could* introduce some logic to write theStr to the file to sync-up with sb_ConcatString(strBufToAssign, theStr), but this seems messy and unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most comments are just going ga-ga over how great this change set is going to be. A few minor tweaky tweakies mixed in.
c/graphLib/io/strOrFile.c
Outdated
int intCandidateIndex = 0; | ||
while ((currChar = sf_getc(theStrOrFile)) != EOF) | ||
{ | ||
if (intCandidateIndex > (MAXCHARSFOR32BITINT - 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this means you don't read integers at or below -1 billion, and that integers up to 10 billion mine 1 are allowed. Need a little work on the bounds checking. Generally, MAXCHARSFOR32BITINT would best be set to 11 and change the comment to exclude counting for the null terminator. Then, in the declaration and memset, use MAXCHARSFOR32BITINT+1 so that the code self-documents your intent to allocate and clear space for the null terminator o a string (bcz a string is separate from the max chars needed to hold a 32 bit int). Then, at this if stmt, it can be a little clearer to instead use >=
bcz intCandidateIndex >= MAXCHARSFOR32BITINT
more clearly says what boundary condition you're hitting. Last but not least, I think you need to catch this on the byte before (or two before if the number has no negative) and see whether the string would blow the 32-bit value space if the last character is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I use %d
in the sscanf()
in sf_ReadInteger()
, I believe it will try to still read the contents of intCandidateStr
as a signed integer and will produce an incorrect result; I was thinking that the updated comment in apiutils.h
for MAXCHARSFOR32BITINT
should be:
// N.B. Every time you're trying to read a 32-bit int from a string,
// you should only need to read this many characters: an optional '-',
// followed by 10 digits (max signed int value is 2,147,483,647). One
// must always allocate an additional byte for the null-terminator!
Does that work? 🎉
…t strncmp() was being made to ioMode. Verified the fix to output to stdout when called from command-line works for SpecificGraph(), RandomGraph(), TransformGraph(), and TestAllGraphs()
…pecify stdin (which is not allowed for any menu-driven access).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great bug fixes on stdout! Just a few tweaks on the tweaks.
* Fixed len arg of memset() in sf_ReadInteger() to fully clear out intCandidateStr. * Updated exitCode logic in g6-read-iterator.c's _ReadGraphFromG6StrOrFile() to match g6-write-iterator's _WriteGraphToG6StrOrFile()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small tweak in latest changes, plus we still have to discuss the ReadInteger() edge cases, which we can do on tomorrow's regular zoom.
…mG6StrOrFile(), and also made sure to endG6WriteIteration() regardless of whether the exitCode was OK in _WriteGraphToG6StrOrFile().
… in _WriteAdjList() and _WriteAdjMatrix() so that it refers to the MAXCHARSFOR32BITINT.
Resolves #57
Added
c/samples/nauty_example.LEDA
- handwritten LEDA format translation of thenauty
example graph from section 20 (p.82) of the manual, which was used to manually test_ReadLEDAGraph()
Updated
Changes to the
strOrFile
container and supportingsf_[a-zA-Z]+()
functionsc/graphLib/io/strOrFile.h
strOrFile
struct
to includestrBufP
fortheStr
, which eliminates the need fortheStrPos
. Also addscontainerType
, which allows us to swiftly check whether the container is anINPUT_CONTAINER
orOUTPUT_CONTAINER
when validating whether a particular operation is applicable to the container type. Finally, theungetBuf
, astackP
, was added to manage unget operations onINPUT_CONTAINER
ssf_New()
to indicate it now accepts a string for the filename to open, if applicable, and a string representing theioMode
, in addition to the optionaltheStr
_Read[a-zA-Z]+()
and_Write[a-zA-Z]+()
functionsc/graphLib/io/strOrFile.c
sf_New()
- If thestrOrFile
container is meant to contain aFILE *
, it now handles opening the file corresponding tofileName
with the correctioMode
(eitherREADTEXT
orWRITETEXT
) and assigns topFile
. This logic also handles when the user specifies some input/output stream (i.e.stdin
,stdout
,stderr
). Otherwise, if the container is meant to hold a string, it initializes astrBufP
and appends the initialtheStr
(as a follow-up to this question, iftheStr
is notNULL
and thecontainerType
isOUTPUT_CONTAINER
, then we error out). Regardless of the container type, anungetBuf
stackP
is allocated, although only in the case ofINPUT_CONTAINER
s do we interact with it: it is used to contain characters that are ungotten (either bysf_ungetc()
orsf_ungets()
) and is consumed first when wesf_getc()
orsf_fgets()
(in this question, I asked whether theungetBuf
should only be allocated forINPUT_CONTAINER
(i.e. whenioMode
isREADTEXT
). Upon further reflection, even if it's not being used, we should allocate theungetBuf
so that there's no illegal memory access issues).sf_getc()
- if theungetBuf
stackP
is nonempty,sp_Pop()
s achar
; otherwise,getc()
from theFILE *
or get the character at the currentReadPos
oftheStr
(astrBufP
).sf_ReadSkipChar()
,sf_ReadSkipWhitespace()
,sf_ReadSingleDigit()
,sf_ReadInteger()
, andsf_ReadSkipInteger()
all usesf_getc()
to advance through thestrOrFile
containersf_ReadSkipLineRemainder()
usessf_fgets()
to advance through the file container and consume up to and including the next\n
sf_ungetc()
-ensures the container is a validINPUT_CONTAINER
beforesp_Push()
ingtheChar
onto theungetBuf
stackP
sf_ungets()
- ensures the container is a validINPUT_CONTAINER
beforesp_Push()
ing each character ofstrToUnget
onto theungetBuf
stackP
in reverse order so that they can be fetched from theungetBuf
in the order of the original string.sf_fgets()
- ensures the container is a validINPUT_CONTAINER
before it obtains as many characters as possible from theungetBuf
before switching over to theFILE *
orstrBufP
in thestrOrFile
container (if no\n
encountered and there's still characters desired by the caller)sf_fputs()
- ensures the container is a validOUTPUT_CONTAINER
before it callsfputs()
if the container has aFILE *
orsb_ConcatStr()
if it containstheStr
.sf_takeTheStr
- now usessb_TakeString()
, sincetheStr
is astrBufP
sf_closeFile()
- if thestrOrFile
contains aFILE *
, it's only appropriate tosp_Free()
theungetBuf
, since it's no longer valid.sf_Free()
- Now callssb_Free()
ontheStr
(astrBufP
) rather thanfree()
(sincetheStr
used to correspond to achar *
), and frees theungetBuf
stackP
usingsp_Free()
if it has not yet been freed.Changes to
strOrFile
dependenciesc/graphLib/io/strbuf.h
- added new macro tosb_GetUnreadCharCount()
: we only want to see how many characters have not yet been read (e.g. used to ensure there's at least one character available to read insf_getc()
andsf_fgets()
)c/graphLib/lowLevelUtils/apiutils.h
- addedMAXCHARSFOR32BITINT
so that, for example,sf_ReadInteger()
can allocate sufficient memory for theintCandidateStr
. Other functions needing to represent 32-bit integers as strings should use this same constant.Using the
strOrFile
container for input and outputThe original intent of this refactor was to restore being able to read from
stdin
, which was lost when we changed how we interrogated the input files (reading an entire line from file vs. reading a single character). As an added bonus of this refactor, the logic ofgp_Read(FromString)?()
andgp_Write(ToString)?()
functions has been simplified and unified in the respective_(Read|Write)Graph()
functions and the logic of the_(Read|Write)[a-zA-Z]+()
functions no longer require branching logic to handle whether or not the(input|output)Str
or(in|out)fileName
wasNULL
.c/graphLib/io/graphIO.c
gp_Read()
- Now creates astrOrFileP
to handle opening the file corresponding toFileName
forREADTEXT
and calls_ReadGraph()
gp_ReadFromString()
- Now creates astrOrFileP
to contain theinputStr
and calls_ReadGraph()
_ReadGraph()
- Applies the relevantsf_[a-zA-Z]+()
functions to interrogate the contents of theinputContainer
to determine the input filetype and to call the corresponding_Read[a-zA-Z]+()
. If extra data is allowed for the input type (i.e. only after successfully reading Adjacency List or Adjacency Matrix), create astrBufP
to contain theextraData
, which is read from theinputContainer
line-by-line usingsf_fgets()
and concatenated usingsb_ConcatString()
.gp_Write()
- Now creates astrOrFileP
to handle opening the file corresponding toFileName
forWRITETEXT
and calls_WriteGraph()
gp_WriteToString()
- Now creates astrOrFileP
withtheStr
initially empty to send to_WriteGraph()
alongside thepOutputStr
; if writing to theoutputContainer
was successful AND if theoutputContainer
has not yet been cleaned up (as is the case for writing Adjacency List and Adjacency Matrix), thensf_takeTheStr()
and assign the pointer to(*pOutputStr)
_WriteGraph()
- Based on theMode
, determines which_Write[a-zA-Z]+()
function should be called and passes in the (dereferenced)outputContainer
. If we're writing to.g6
, then_WriteGraphToG6StrOrFile()
will have already taken the string from theoutputContainer
and assigned it topOutputStr
, so we null out the reference tooutputContainer
to preventgp_WriteToString()
from trying to take the string again from theoutputContainer
, or to double-free theoutputContainer
. Then, if extra data is allowed to be written (i.e. assuming we have rungp_Embed()
ontheGraph
with a graph algorithm extension attached for whichfpWritePostprocess()
has been overridden, e.g._DrawPlanar_WritePostprocess()
), generates the extra data as achar *
and usessf_fputs()
to write it to theoutputContainer
._ReadAdjMatrix()
,_ReadAdjList()
,_ReadLEDAGraph()
,_WriteAdjList()
,_WriteAdjMatrix()
, and_WriteDebugInfo()
all updated so that they accept only the input/outputstrOrFile
container and perform the relevantsf_[a-zA-Z]+()
functions for read/write operations.c/graphLib/io/g6-read-iterator.c/.h
beginG6ReadIterationFromG6FilePath()
,beginG6ReadIterationFromG6FilePointer()
, andbeginG6ReadIterationFromG6String()
in favour of the newbeginG6ReadIterationFromG6StrOrFile()
_ReadGraphFromG6FilePointer()
and updated_ReadGraphFromG6FilePath()
and_ReadGraphFromG6String()
helper functions to call the new_ReadGraphFromG6StrOrFile()
helper function.beginG6ReadIterationFromG6StrOrFile()
function validates theg6InputContainer
and assigns it to thepG6ReadIterator
before calling_beginG6ReadIteration()
_beginG6ReadIteration()
now initializescharConfirmation
toEOF
(since value ofEOF
is implementation-dependent, although it's commonly set to-1
)endG6ReadIteration()
now no longer "knows" whether theg6Input
strOrFileP
holds a string or a file, so remove the explicit call tosf_closeFile()
and allowsf_Free()
to clean that up._ReadGraphFromG6FilePath()
now creates aninputContainer
to handle opening the file corresponding to thepathToG6File
before calling_ReadGraphFromG6StrOrFile()
_ReadGraphFromG6String()
now creates aninputContainer
to handle reading the graph from theg6EncodedString
before calling_ReadGraphFromG6StrOrFile()
_ReadGraphFromG6StrOrFile()
helper function accepts theg6InputContainer
, ensures it's valid, and then allocates theG6ReadIterator
before callingbeginG6ReadIterationFromG6StrOrFile()
and proceeding with the remaining flow (i.e.readGraphUsingG6ReadIterator()
,endG6ReadIteration()
, andfreeG6ReadIterator()
)c/graphLib/io/g6-write-iterator.c/.h
beginG6WriteIterationToG6FilePath()
,beginG6WriteIterationToG6FilePointer()
, andbeginG6WriteIterationToG6String()
in favour of the newbeginG6WriteIterationToG6StrOrFile()
_WriteGraphToG6FilePointer()
and updated_WriteGraphToG6FilePath()
and_WriteGraphToG6String()
helper functions to call the new_WriteGraphToG6StrOrFile()
helper function.beginG6WriteIterationToG6StrOrFile()
function validates theoutputContainer
and assigns it to thepG6WriteIterator
before calling_beginG6WriteIteration()
endG6WriteIteration()
now no longer "knows" whether theg6Output
strOrFileP
holds a string or a file, so remove the explicit call tosf_closeFile()
and allowsf_Free()
to clean that up.g6Output
contains a string, it's expected that you must havesf_TakeTheString()
before ending write iteration, otherwisetheStr
gets cleaned up._WriteGraphToG6FilePath()
now creates anoutputContainer
to handle opening the file corresponding to theg6OutputFilename
before calling_WriteGraphToG6StrOrFile()
(which now owns that memory)_WriteGraphToG6String()
now creates anoutputContainer
to pass into_WriteGraphToG6StrOrFile()
(owns that memory) alongside the pointer-pointerg6OutputStr
_WriteGraphToG6StrOrFile()
helper function accepts theoutputContainer
, ensures it's valid, and then allocates theG6WriteIterator
before callingbeginG6WriteIterationToG6StrOrFile()
and proceeding with the remaining flow (i.e.writeGraphUsingG6WriteIterator()
, taking the string from theoutputContainer
ifoutputStr
is notNULL
,endG6WriteIteration()
, andfreeG6WriteIterator()
)c/planarityApp/planarityRandomGraphs.c
This refactor was primarily due to the fact that
beginG6WriteIterationToG6FilePath()
was deprecated. So then the two branches that allow us to write to.g6
had to be updated:RandomGraphs()
is called with non-NULLoutfileName
, it must have been called bycallRandomGraphs()
where the user specified the optionalO
parameter via the command-line. TheoutputContainer
is initialized with thisoutfileName
to handle opening the file object for this path for the command-line-driven branch.outfileName
isNULL
, then we check to see ifRandomGraph()
was called via the menu-driven system after choosing toReconfigure()
withr
("Randomly generate graphs"),y
("Do you want original graphs in directory 'random'?"), andg
("Do you want to output generated graphs to ... G6 (all)?"). If so, then we constructtheFileName
based on the order of the graphs and the number to be generated and initialize theoutputContainer
withtheFileName
.Afterwards, if the
G6WriteIterator
andoutputContainer
have been allocated, then webeginG6WriteIterationToG6StrOrFile()
and willwriteGraphUsingG6WriteIterator()
during the random graph generation loop.c/planarityApp/planarityTestAllGraphs.c
TestAllGraphs()
, we create theinputContainer
from theinfileName
and allow thestrOrFile
machinery to handle opening the file and creating the file pointer. Then, this is passed totestAllGraphs()
, which sets upg6
read iteration from thisstrOrFileP
usingbeginG6ReadIterationFromG6StrOrFile()
. This means that one may pipe.g6
-encoded graphs in fromstdin
to test:outputTestAllGraphsResults()
, we create thetestOutput
strOrFileP
to simplify handling whether we're outputting to a file with pathoutfileName
or tooutputStr
. Once theheaderStr
andresultsStr
are written to whatever output viasf_fputs()
, only ifoutputStr
was non-NULL do we try tosf_takeTheString()
from thetestOutput
and assign that pointer to the memory to whichoutputStr
points. This means that one may still output the results ofTestAllGraphs()
to file or to string.Changing the type of
extraData
@john-boyer-phd explains the original motivation behind
extraData
havingvoid *
type and the choice to move to usingchar *
in this comment:c/graphLib/extensionSystem/graphFunctionTable.h
- updatedgraphFunctionTable
struct's function pointers' signatures forfpReadPostprocess()
andfpWritePostprocess()
to correspond toextraData
being a null-terminatedchar *
(i.e.extraDataLength
is no longer required)c/graphLib/graphUtils.c
- updated default graph algorithm extension functions_ReadPostprocess()
and_WritePostprocess()
function signatures to correspond toextraData
being a null-terminatedchar *
c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c
_ReadPostprocess()
(i.e._DrawPlanar_ReadPostprocess()
) and_WritePostprocess()
(i.e._DrawPlanar_WritePostprocess()
) to correspond toextraData
being a null-terminatedchar *
_DrawPlanar_ReadPostprocess()
, we can now takestrlen(extraData)
, since it must be a null-terminatedchar *
, and no longer need to rely on supplementary variableextraDataSize
. No longer need to castextraData
as achar *
to advance the pointer beyond the textual<DrawPlanar>
start tag and re-cast tovoid *
(extraData
's original type), since we are guaranteed thatextraData
is achar *
_DrawPlanar_WritePostprocess()
, theextraData
is nowcalloc
'ed instead ofmalloc
'ed to guarantee the memory allocated is zeroed out (since\0
is a control character with value zero, this means any string we construct is guaranteed to be null-terminated)Resolving MSVC compiler warnings
c/planarityApp/planarityMenu.c
andc/planarityApp/planarityUtils.c
- sincetolower()
returns anint
rather than achar
, explicitly cast the result tochar
c/planarityApp/planarityRandomGraphs.c
- inRandomGraphs()
, explicitly assignoutputContainer
NULL
when declaredc/graphLib/io/g6-read-iterator.c
- Explicitly cast result ofstrlen()
toint
(fromsize_t
) in comparisons, and castfirstChar
andgraphChar
tochar
when wesf_ungetc()
c/graphLib/io/g6-write-iterator.c
- explicitly cast conversion ofgraphOrder
to anASCII
character