The following issues have been identified with zstring data, but it can be assumed that this also occurs for wstring data.
2 test programs that highlight the bad behavior of 'PUT/GET (File I/O)' when putting/getting zstring data:
a) 'PUT (File I/O)' when putting zstring data:
#include "file.bi"
Dim As Zstring * (15+1) zbuffer = "Hello FreeBASIC" '' 15 string character data
Dim As Zstring Ptr pzbuffer = @zbuffer
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , zbuffer, 5)' (0 = success): " & Put(#99, , zbuffer, 5)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , zbuffer)' (0 = success): " & Put(#99, , zbuffer)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , *pzbuffer, 5)' (0 = success): " & Put(#99, , *pzbuffer, 5)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , *pzbuffer)' (0 = success): " & Put(#99, , *pzbuffer)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , zbuffer[0], 5)' (0 = success): " & Put(#99, , zbuffer[0], 5)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Open "test.bin" For Binary Access Write As #99
Print "returned by 'Put(#99, , (*pzbuffer)[0], 5)' (0 = success): " & Put(#99, , (*pzbuffer)[0], 5)
Close #99
Print " file lenght: " & Filelen("test.bin")
Print
Sleep
b) 'GET (File I/O)' when getting zstring data:
(the most delicate in this last test program is to master the initial element values of the zstring buffer)
Open "test.bin" For Binary Access Write As #99
Put #99, , "Hello FreeBASIC"
Close #99
Dim As Zstring * 33 zbuffer
Dim As Zstring Ptr pzbuffer = @zbuffer
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'get(#99, , zbuffer, 5)' (0 = success): " & get(#99, , zbuffer, 5), , "in zbuffer cleared"
Close #99
Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'get(#99, , zbuffer)' (0 = success): " & get(#99, , zbuffer), , "in zbuffer cleared"
Close #99
Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , *pzbuffer, 5)' (0 = success): " & Get(#99, , *pzbuffer, 5),, "in zbuffer cleared"
Close #99
Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer cleared"
Close #99
Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33) : zbuffer = Space(5)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(5)"
Close #99
Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33) : zbuffer = Space(10)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(10)"
Close #99
Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33) : zbuffer = Space(32)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(32)"
Close #99
Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , zbuffer[0]), 5' (0 = success): " & Get(#99, , zbuffer[0], 5), "in zbuffer cleared"
Close #99
Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer)
Print
Clear(zbuffer[0], 0, 33)
Open "test.bin" For Binary Access Read As #99
Print "returned by 'Get(#99, , (*pzbuffer)[0], 5)' (0 = success): " & Get(#99, ,(*pzbuffer)[0], 5), "in zbuffer cleared"
Close #99
Print " '" & zbuffer & "'",, "zbuffer length: " & Len(zbuffer)
Print
Sleep
a) 'PUT (file I/O)' behavior summary:
- When a real zstring variable (zbuffer) is passed, the behavior should be the same than for a fix-len string (dim as string * N ...). So the amount parameter should be forbidden. Otherwise, it is dangerously used to multiply the string length to be written to the file, but possibly by overflowing outside the provided zstring buffer. => NOK
- When a dereferenced zstring pointer (*pzbuffer) is passed, the amount parameter is also authorized but seems to be ignored because the pointed buffer is written to the file up to the zero element (terminal element which is excluded). => NOK
- When an ubyte (zbuffer[0] or (*pzbuffer)[0]) is passed, the behavior is correct with the amount parameter well used. => OK
b) 'GET (file I/O)' behavior summary:
- When a real zstring variable (zbuffer) is passed, the behavior should be the same than for a fix-len string (dim as string * N ...). So the amount parameter should be forbidden. Instead, it is ignored (except for the '0' value). => NOK
- When a dereferenced zstring pointer (*pzbuffer) is passed, the amount parameter is also authorized but seems to be ignored. But to work, the pointed buffer must begin with at least as many non-zero elements as the number of elements to read. => NOK
- When an ubyte (zbuffer[0] or (*pzbuffer)[0]) is passed, the behavior is correct with the amount parameter well used. => OK
See also discussion on forum ('Trouble with file I/O put command' topic in General)
https://www.freebasic.net/forum/viewtopic.php?f=3&t=29688
1) For 'PUT (File I/O)' and 'GET (File I/O)', the amount parameter must be forbidden when any [w/z]string is passed (a real [w/z]string variable or a dereferenced [w/z]string pointer).
2) For 'GET (File I/O)' and a dereferenced [w/z]string pointer passed, the write process to the buffer must work whatever the initial element values of the pointed buffer (as it does for a real [w/z]string variable passed).
When the zstring buffer is dynamically allocated, only a 'zstring ptr' is directly available, so this bug is encountered when using 'GET (file I/O)':
Therefore for this bad 'GET (file I/O)' behavior, a workaround (other than initializing the values of the buffer elements) is to use an equivalent zstring structure in a Type, and place it to the address of the dynamically allocated memory:
Last edit: fxm (freebasic.net) 2021-11-08