Hi Guys .
I’am here to get help from a strange behaviour i stumbled on using a combination of _ = stdin.streamDelimiter and allocator.free()… this is the snippet code and the error i got.
try prompt(stdout, stdin, &stdinBuf, "Insert the string to search: ", stringToFind);
stdin.toss(1); // skip the newline
stringToFind = try stdinBuf.toOwnedSlice();
defer allocator.free(stringToFind);
so , i entered the “d:\pppp” string which is 7 char + ‘\r’ = 8 chars on windows 11
after that i have the code to remove ‘\r’ with stringToFind = std.mem.trimEnd(u8, stringToFind, "\r");
but when i try to close the prorgram i got this
error(gpa): Allocation size 8 bytes does not match free size 7. Allocation:
D:\Programmi\ZIG\lib\std\array_list.zig:692:52: 0x7ff750b9732b in toOwnedSlice (ChangeRegKEYS_zcu.obj)
const new_memory = try gpa.alignedAlloc(T, alignment, self.items.len);
^
D:\Programmi\ZIG\lib\std\Io\Writer.zig:2603:33: 0x7ff750b94281 in toOwnedSlice (ChangeRegKEYS_zcu.obj)
return list.toOwnedSlice(a.allocator);
^
D:\Progetti_ZIG\RegistryUpdate2\src\main.zig:193:45: 0x7ff750b92d0d in main (ChangeRegKEYS_zcu.obj)
stringToFind = try stdinBuf.toOwnedSlice();
it’s clear that either I do not remove the ‘r’ char ( to be compliant with .free() ) or i’m not able to manage the string.
Is my opinion that the correct way to manage this behaviout shoul be this _ = stdin.streamDelimiter(&buf.writer, '\r\n') catch |err| {
Unlike in C, Zig allocators assume that you pass a pointer and the correct length to the free function.
This allows the allocator to be implemented more efficiently since it needs to store less meta data to determine the size of the allocation.
This is safety-checked by the debug allocator.
In your case I’d propose to keep the string managed by the ArrayList:
var stdinBuf: … = …;
defer stdinBuf.deinit(allocator);
…
try prompt(…);
stringToFind = stdinBuf.items;
stringToFind = std.mem.trimEnd(…); // It’s safe to trim, since the list keeps track of the pointer + capacity
Hi , thank for your replay.
I think that your suggestione it’s a patch ( a work around) not a solution. I can not work with your suggestion with all projects.
stated the code std.mem.trimEnd(u8, cmd_opt, " \r\n")
what is the difficult to have the same behaviour on _ = stdin.streamDelimiter(&buf.writer, '\r\n') catch |err
Is windows considered the last wagon’s wheel..?
last thing , I have no idea how i can change from stdin.streamDelimiter ( ...) to another method using ArrayList as buffer ( i’m using 0.15.2)…could you give me a concrete example just to give it a sight. ?
thank
The problem is like this: this function returns a slice of the input buffer, so the returned result does not have ownership. Therefore, if your stringToFind is an owned slice, doing this will cause the original ownership pointer to be lost.
In general, owned slices should not be var, otherwise it is easy to lose their ownership. The correct approach is to use a separate variable to store the result of trimEnd.
You’ll need to, this is how allocations work in Zig. You get a slice, that’s a pointer and a length, you give back the same slice.
Zig’s way is better, but it will confound your expectations, coming from a background of using C. I know I barked my shins on it a few times. It is better though, you’ll see.
Surely you can use defer stdinBuf.deinit() instead of making the ArrayList an owned slice and then freeing it?
You could still easily get the string by just accessing the items member, and still trim it with std.mem.trimEnd, but the difference would be that instead of trying to free the smaller sub-slice created by std.mem.trimEnd, the .deinit() call from earlier would be freeing the ENTIRE slice, no matter what.
You have to keep track of all the allocated memory, no way around it.
The result is you will have to store the whole string, in addition to the subs lice you are mostly working with.
but you dont need a new variable for that, stdinBuf already has the whole string, just dont call toOwnedSlice as that empties stdinBuf.
instead call trim directly on stdinBuf.items and store that result.
and dont forget to deinit stdinBuf
Hi , thank to all for suggestions, but unfortunately is still not clear what do you mean.
I think you all are talking about using Arraylist ( that is exactly what i would not like).
ok, but that said , what could be a concrete example to give a sight ?
Also it must be considered that the code i postsed as try prompt(stdout, stdin, &stdinBuf, "Insert the string to search: ", . . . .
is a generic function that i made up just to have a generic parm from the user.
so the assingnment to real string or what else must be external to ‘prompt’ and also the used buffer ( arraylist or other) must be cleared before every new read parm.
For what i know the definition for an arraylisy should be like var stIbuf = try std.ArrayList(u8).initCapacity(allocator, 128); defer stIbuf.deinit(allocator); <<< just one time on main
and if this is true , what could be the command to clear data buffer before every new read parm ?
and what command to use to get user input using arraylist buffer ?
stringToFind and trimmed_string point to the same memory. trimmed_string has the region you’re interested in, stringToFind has the region the allocator wants back. Simple.
Ok , thank , but i don’t want to do that. As i supposed this is a work around. not a solution.
i want to reuse the same string. otherwise how many strings do i have to define for several input string.. ??. Also i have to manage all the cases between linux /windows … this became very verbose.
one very simple solution could be stringToFind = try stdinBuf.toOwnedSlice().trimEnd(u8, "\r" );
I’m not against arraylist if any one show me a generic function that could solve the problem i posted.
It is the same string. It’s a different view of it, but it’s the same string. That’s why this works.
Zig is not the ideal language if terseness is among your terminal goals.
You’re learning a new language. You have to meet that language on its own terms. The attitude you bring to that task is up to you, but it will also influence how successful you are likely to be at mastering it.
As mentioned, it is the same string buffer. You want the same buffer around for your next read, of course, but that means that you have to have done something with the prior contents, such that you’re not concerned with the buffer being overwritten. That is, maybe your concern is with what lurks beyond the snippet you posted, in your use of the captured string itself. If, for instance, you just std.debug.print() it, great, all done. If you copy it to another location, like a field of a struct for your program, great, again, all done. But if you just stored the reference to the slice, for later processing, then you would indeed get a surprise later. But perhaps all that is obvious and we’re missing something else you’re trying to share here.
EDIT: sorry, I missed that you were trying toOwnedSlice() in the mix, right from the start; that has implications, and hints into the “beyond” that I thought was hidden, indeed. Various suggestions of others seem to point the right way (e.g., @vulpesx mentioned “instead call trim directly on stdinBuf.items and store that result.”), and my gist is still valuable, perhaps, as you seem to be processing at that level of “ownership”
This can’t work because allocator.free() must get the result of .toOwnedSlice() and cannot get the one with a different length.
As an alternate phrasing that keeps the intent of your “stringToFind”, maybe this makes more sense?
edit
Since stringToFree is created immediately after the allocation and it is const, it is guaranteed that the allocation and the .free() call match exactly. (Presuming stdinBuf’s internal allocator is the same allocator, which I presume is the case but can’t tell from this code snippet.)
Since stringToFind is then created without an allocator, it does not need to/cannot be freed.
If this type of memory management is too tedious, then I recommend learning how to use an ArenaAllocator.
still thanks to all,
i was wrong when i said “i want to reuse the same string.” , my meaning was i want to use the same definition ( i know that slice is pointing to the same string ).
after this , i tried to go deep with “can really use arraylist as buffer for my stdin.streamDelimiter(&buf.writer, …. “ ?? , and of course the answer is NO!, because you must have a std.io.Writer that arraylist doesn’t. so for any one that suggested to use arraylist should be nice to have an example.
the final work around ( not a solution) i found is this one even though i don’t like. stringToFind = try allocator.dupe(u8, std.mem.trimEnd(u8, stdinBuf.written(), “\r”)); defer allocator.free(stringToFind);
it’s a shame , i liked very much the proposed solution to make stdin.streamDelimiter(&buf.writer, '\r\n')
it would have simplified all.
In the interest of being as clear as possible, it works if the slice is not mutated. It doesn’t work if that variable is replaced with something else which has the wrong length.
Edit: I see now that the trimEnd is directly after the toOwnedSlice, right precisely where my view cuts it off. So what we’re both saying is you need to keep the slice before the end is trimmed.
”…need to keep the slice before the end is trimmed…”
my code was just an example that i liked to think. this bad experience let me realize that to keep things “simple” this is the right grammar to be implement by the team , and it applies for all environments.
I had assumed you were using array list as that’s the most recognisable type with toOwnedSlice. But seeing you mention .written() it seems that you are using the allocating writer.
On either an array list or the allocating writer, you can use clearRetainCapacity() which empties the list, but keeps the allocation so that its next usage may not have to allocate.
To get the contents of an array list, it’s just stdinBuf.items.
For the allocating writer, it is .written()
if you are using the allocating writer, it seems your prompt function takes a pointer to it *std.Io.Writer.Allocating. It should instead take a pointer to the interface *std.Io.Writer, then you would give the function &stdinBuf.writer
This lets you use different writers, which makes your code more useable, and easier to test.
As a side note, you could now switch your writer to be a std.Io.Writer.fixed (which is the only one that gives you the interface directly instead of a wrapper type).
The benefit of doing so is using stack memory and having a fixed upper bound. Even if there is no technical limit, it is good practice to give things a reasonable limit.
It’s also possible you do need to support arbitrarily large inputs, in which case the allocating writer is what you want.
Hi , sorry I’am in late.
Thank to your answer , but ii think , in general, that we are circunventing the problem.
firt , yes i am using *std.Io.Writer.Allocating just because i can’t use arraylist with try stdin.streamDelimiter(…
if you have any example using arraylist you are welcome.
second, the problem is not if I use *std.Io.Writer.Allocating or *std.Io.Writer
but that the streamDelimiter doesn’ have the possibilities to set two chars to define delimiter , as for example : stdin.streamDelimiter(&buf.writer, ‘\r\n’) and so the only workaround i found is stringToFind = try allocator.dupe(u8, std.mem.trimEnd(u8, stdinBuf.written(), “\r”));
is evident that the standard stdin.streamDelimiter(…’\n’…) is usefull only in linux env.
so, i thank for the help , but doesn’t solve the main problem.
the problem was you weren’t keeping your allocated memory accessible so you could free it, the only solution is to keep it accessible in some way such as what I suggested, but an arena also works (which is just tracking it itself)
yes, this is circumventing the problem, but circumventing is still a solution. There is no solution that doesn’t circumvent it.
I was suggesting passing just an *std.Io.Writer to your function, you can still use the allocating implementation. Passing the interface instead just makes it more flexible.
std.Io.Writer.Allocating has replaced the support for ArrayList in the previous api, this is simply because a seperate type is needed to implement the interface. While ArrayList could implement it, but it would increase its size which is a problem if you are have a lot of them. The solution was just to seperate the the implementation to a different type. That is what Writer.Allocating is, it has functions to go to and from an arraylist if you need/want that.
this is a different problem that what you started this topic for, but I’ll answer anyway.
Keep in mind that zig is WIP and std largely caters to the compilers needs. Detecting CRLF is only a couple more lines of code, which you could make a wrapper function for.
If you want to create a proper implementation, it’s actually quite easy, you just copy the current one and change the delimiter to be a slice as well as indexOfScalar to indexOf.