strbuf `filter' API
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 Fix in-place editing in crlf_to_git and ident_to_git.
When crlf_to_git or ident_to_git are called "in place", the buffer already is big enough and should not be resized (as it could make the buffer address change, hence invalidate the `src' pointers !).
Also fix the growth length at the same time: we want to replace the buffer content (not append) in those functions as they are filters.
Thanks to Bernt Hansen for the bug report.
---
This patch is done on top of master, as strbuf's have been merged.
This is a major issue.
convert.c | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-)
convert.c +8 -4
@@ -110,7 +110,9 @@ static int crlf_to_git(const char *path, const char *src, size_t len,return 0 ; } -strbuf_grow (buf, len); +/* only grow if not in place */ +if (src != buf->buf) +strbuf_grow (buf, len - buf->len); dst = buf->buf;if (action ==CRLF_GUESS ) {/*
@@ -281,12 +283,12 @@ static int apply_filter(const char *path, const char *src, size_t len, ret =0 ; }if (close (pipe_feed[0 ])) { - ret =error ("read from external filter %s failed" , cmd); +error ("read from external filter %s failed" , cmd); ret =0 ; } status =finish_command (&child_process);if (status) { - ret =error ("external filter %s failed %d" , cmd, -status); +error ("external filter %s failed %d" , cmd, -status); ret =0 ; }
@@ -422,7 +424,9 @@ static int ident_to_git(const char *path, const char *src, size_t len,if (!ident || !count_ident (src, len))return 0 ; -strbuf_grow (buf, len); +/* only grow if not in place */ +if (src != buf->buf) +strbuf_grow (buf, len - buf->len); dst = buf->buf;for (;;) { dollar =memchr (src,'$' , len);
Signature · 1 line
1.5.3.4.207.gc79d4-dirty
PATCH 1/1 Fix memory leak in apply_filter.
---
While we're at it... Here is a stupid memory leak in apply_filter. On top of the previous commit.
convert.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-)
convert.c +2 -3
@@ -293,10 +293,9 @@ static int apply_filter(const char *path, const char *src, size_t len, }if (ret) { - *dst = nbuf; - }else { -strbuf_release (&nbuf); +strbuf_swap (dst, &nbuf); } +strbuf_release (&nbuf);return ret; }
Signature · 1 line
1.5.3.4.208.gdcc67-dirty
PATCH 1/1 Fix memory leak in apply_filter.
In reply to Pierre Habouzit---
While we're at it... Here is a stupid memory leak in apply_filter. On top of the previous commit.
The same, repost, unsigned doh.
convert.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-)
convert.c +2 -3
@@ -293,10 +293,9 @@ static int apply_filter(const char *path, const char *src, size_t len, }if (ret) { - *dst = nbuf; - }else { -strbuf_release (&nbuf); +strbuf_swap (dst, &nbuf); } +strbuf_release (&nbuf);return ret; }
Signature · 1 line
1.5.3.4.208.gdcc67-dirty
PATCH 1/1 Fix in-place editing functions in convert.c
In reply to Pierre Habouzit* crlf_to_git and ident_to_git:
Don't grow the buffer if there is enough space in the first place. As a side effect, when the editing is done "in place", we don't grow, so the buffer pointer doesn't changes, and `src' isn't invalidated anymore.
Thanks to Bernt Hansen for the bug report.
* apply_filter:
Fix memory leak due to fake in-place editing that didn't collected the old buffer when the filter succeeds. Also a cosmetic fix.
---
This patch is on top of master, and supersedes both patch I sent before. Following dscho's remark, I only grow the buffer if they aren't big enough in the first place, which ensures that buffers are not touched if edited in place.
convert.c | 17 ++++++++++------- 1 files changed, 10 insertions(+), 7 deletions(-)
convert.c +10 -7
@@ -110,7 +110,9 @@ static int crlf_to_git(const char *path, const char *src, size_t len,return 0 ; } -strbuf_grow (buf, len); +/* only grow if not in place */ +if (strbuf_avail (buf) + buf->len < len) +strbuf_grow (buf, len - buf->len); dst = buf->buf;if (action ==CRLF_GUESS ) {/*
@@ -281,20 +283,19 @@ static int apply_filter(const char *path, const char *src, size_t len, ret =0 ; }if (close (pipe_feed[0 ])) { - ret =error ("read from external filter %s failed" , cmd); +error ("read from external filter %s failed" , cmd); ret =0 ; } status =finish_command (&child_process);if (status) { - ret =error ("external filter %s failed %d" , cmd, -status); +error ("external filter %s failed %d" , cmd, -status); ret =0 ; }if (ret) { - *dst = nbuf; - }else { -strbuf_release (&nbuf); +strbuf_swap (dst, &nbuf); } +strbuf_release (&nbuf);return ret; }
@@ -422,7 +423,9 @@ static int ident_to_git(const char *path, const char *src, size_t len,if (!ident || !count_ident (src, len))return 0 ; -strbuf_grow (buf, len); +/* only grow if not in place */ +if (strbuf_avail (buf) + buf->len < len) +strbuf_grow (buf, len - buf->len); dst = buf->buf;for (;;) { dollar =memchr (src,'$' , len);
Signature · 1 line
1.5.3.4.207.gb504-dirty
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 4 lines · Pierre Habouzit
convert.c +0
-0@@ -281,20 +283,19 @@ static int apply_filter(const char *path, const char *src, size_t len, ret =0 ; }if (close (pipe_feed[0 ])) {
convert.c static int apply_filter(const char *path, const char *src, size_t len, · line 293
Quoted 10 lines
convert.c +2
-2- ret =error ("read from external filter %s failed" , cmd); +error ("read from external filter %s failed" , cmd); ret =0 ; } status =finish_command (&child_process);if (status) { - ret =error ("external filter %s failed %d" , cmd, -status); +error ("external filter %s failed %d" , cmd, -status); ret =0 ; }
If you want to, you can leave away these cosmetical corrections since I'm working on a patch that replaces this entire passus. (Will be needed for the MinGW port).
-- Hannes
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
In reply to Pierre HabouzitThis fixes it for me. Thanks!!
Bernt
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
In reply to Pierre Habouzitconvert.c static int crlf_to_git(const char *path, const char *src, size_t len, · line 115
Quoted 5 lines · Pierre Habouzit
convert.c +3
-1-strbuf_grow (buf, len); +/* only grow if not in place */ +if (strbuf_avail (buf) + buf->len < len) +strbuf_grow (buf, len - buf->len);
Umm. This is really ugly.
The whole point of strbuf's was that you shouldn't be doing your own allocation decisions etc. So why do it?
Wouldn't it be much better to have a strbuf_make_room() interface that just guarantees that there is enough room fo "len"?
Otherwise, code like the above would seem to make the whole point of a safer string interface rather pointless. The above code only makes sense if you know how the strbuf's are internally done, so it should not exists except as internal strbuf code. No?
Linus
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 10 lines · Linus Torvalds
convert.c static int crlf_to_git(const char *path, const char *src, size_t len, · line 115
Quoted 5 lines · Pierre Habouzit
convert.c +3
-1-strbuf_grow (buf, len); +/* only grow if not in place */ +if (strbuf_avail (buf) + buf->len < len) +strbuf_grow (buf, len - buf->len);Umm. This is really ugly.
I agree.
Quoted 2 lines
The whole point of strbuf's was that you shouldn't be doing your own allocation decisions etc. So why do it?
the point here is that it's in a "filter" that is called like this:
some_filter(buf->buf, buf->len, buf);
src len dst
You can call the filter with src/len being data from anywere,
including the current content of the destination buffer.
Then there is two cases, either the filter is known to be done in
place, either we can't know or we know it wont.
In the latter case, we have a bit of code like that:
char *to_free = NULL;
if (buf->buf == src)
to_free = strbuf_detach(&buf);
.. hack ..
free(to_free); In the former case, then there is a small glitch, being that if we are
doing in place editing, we should not touch buffer at all (or it would invalidate "src"). If we are not in the in-place editing code though, then we have to make the resulting buffer be big enough...
Quoted 2 lines
Wouldn't it be much better to have a
strbuf_make_room()interface that just guarantees that there is enough room fo "len"?
Right, that would do the same btw ;)
Quoted 4 lines
Otherwise, code like the above would seem to make the whole point of a safer string interface rather pointless. The above code only makes sense if you know how the strbuf's are internally done, so it should not exists except as internal strbuf code. No?
Well, the above code is used in filters to spare reallocations. So if
we want to "blackbox" such a think, strbuf_make_room isn't the proper API. We should rather use
void *strbuf_begin_filter(struct strbuf *sb, const char *src, size_t reslen); strbuf_end_filter(void *);
strbuf_begin_filter would decide upon the hint reslen argument if
we know if we can work in place or not (has a meaning iff src points into the strbuf buffer). If not, it could stash the strbuf buffer in the returned void * to be freed at the end of the filter. It seems like a better alternative than a strbuf_make_room.
Of course, strbuf_begin_filter() would really be simple and basically
be:
char *tmp;
if (src points into sb->buf && reslen > sb->alloc - 1) {
// in place editing is OK
return NULL;
}
tmp = strbuf_release(&sb);
strbuf_grow(&sb, len);
return tmp; and strbuf_end_filter would just be "free" :)
We could even make "reslen" be a ssize_t so that -1 would mean "I've
absolutely no idea how much space I'll need (or just in place editing is not supported). This way, both hacks I described in this mail could be hidden in the strbuf module, and be properly documented _and_ safe _and_ efficient.
What do you think ?
[Though if we do that, I still think it's more important to fix the bug in master, and have a new patch implementing this approach]
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Ooookay,
I wasn't more thrilled about the previous patch than linus is. Here is
a possible API to hide the muck in the strbuf.c module, and have a way nicer to read code in convert.c.
It remais as efficient as the previous code, tries to reuse the buffer
as much as possible to avoid reallocations, and deal with even more cases (it's possible to have the "source" buffer be inside the destination strbuf now, which could sometimes help a lot).
I based this on top of master (because it's where my previous patch
was) but it should apply on next properly. It depends upon the previous patch though.
I'd say that the previous patch should go in master ASAP, and that
those two could be considered for next (having merged master in it first btw).
Cheers,
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Pierre Habouzit, Fri, Oct 05, 2007 22:19:30 +0200:
Quoted 2 lines
Those are helpers to build functions that transform a buffer into a strbuf, allowing the "buffer" to be taken from the destination buffer.
They are horrible. And very specialized for these "filter" routines. To the point where I would move them into the file where they are used (convert.c only, isn't it?)
If you continue to insist the code is generic enough to justify its residence in strbuf.c, continue reading.
First off, what was wrong with dumb
void strbuf_make_room(struct strbuf *, size_t newsize);
again?
Review
Quoted 10 lines
(quoted diff) +10
-0+void *strbuf_start_filter(struct strbuf *sb, const char *src, ssize_t hint) +{ + if (src < sb->buf || src >= sb->buf + sb->alloc) { + if (hint > 0 && (size_t)hint >= sb->alloc) + strbuf_grow(sb, hint - sb->len); + return NULL; + } + + if (hint < 0) + return strbuf_detach(sb, NULL);
what is that for? Why can't the caller just use strbuf_detach? (He already has to pass negative hint somehow, which should be a concious action).
Review
Quoted 1 line
(quoted diff) +1
-0+
trailing space (two HTs)
Review
Quoted 8 lines
(quoted diff) +8
-0+ if ((size_t)hint >= sb->alloc) { + void *tmp = strbuf_detach(sb, NULL); + strbuf_grow(sb, hint); + return tmp; + } + + return NULL; +}
How can one know when it sb is safe to use after strbuf_end_filter? It is for the first "if", for example. free() wont free the buf in sb. Oh, right, one can check if returned pointer !NULL. Which just adds more code to handle your API.
What actually happens to sb? Is it detached? Is it reallocated? When it is detached and when it is reallocated?
Why is the returned pointer useful only for giving it to strbuf_end_filter?
Take for example your change in crlf_to_git:
@@ -85,6 +85,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
{
struct text_stat stats;
char *dst;
+ void *tmp;
if ((action == CRLF_BINARY) || !auto_crlf || !len)
return 0;
@@ -110,9 +111,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
return 0;
}
- /* only grow if not in place */
- if (strbuf_avail(buf) + buf->len < len)
- strbuf_grow(buf, len - buf->len);
+ tmp = strbuf_start_filter(buf, src, len);
dst = buf->buf;
if (action == CRLF_GUESS) {
/*
@@ -133,13 +132,14 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
} while (--len);
}
strbuf_setlen(buf, dst - buf->buf);
+ strbuf_end_filter(tmp);
return 1;
}
And try to rewrite it with the strbuf_make_room:
@@ -110,9 +111,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
return 0;
}
- /* only grow if not in place */
- if (strbuf_avail(buf) + buf->len < len)
- strbuf_grow(buf, len - buf->len);
+ strbuf_make_room(buf, len);
dst = buf->buf;
if (action == CRLF_GUESS) {
/*
The change looks rather simple
Review
Quoted 3 lines
(quoted diff) +3
-0+/*----- filter API -----*/ +extern void *strbuf_start_filter(struct strbuf *, const char *, ssize_t); +extern void strbuf_end_filter(void *p);
I find the naming very confusing: what filtering takes place where? If strbuf_end_filter is just free, why is it needed at all? For the sake of wrapping free()?
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Quoted 7 lines · Alex Riesen
Pierre Habouzit, Fri, Oct 05, 2007 22:19:30 +0200:
Quoted 2 lines
Those are helpers to build functions that transform a buffer into a strbuf, allowing the "buffer" to be taken from the destination buffer.
They are horrible. And very specialized for these "filter" routines. To the point where I would move them into the file where they are used (
convert.conly, isn't it?)
For now they are, but moving them into convert.c is at the very least
awkward.
Quoted 8 lines
If you continue to insist the code is generic enough to justify its residence in
strbuf.c, continue reading.First off, what was wrong with dumb
void strbuf_make_room(struct strbuf *, size_t newsize);again?
If newsize is >= sb->alloc then the area is reallocated, the pointer
may move, and the "src" pointer would then be invalidated.
Quoted 6 lines
Quoted 6 lines
(quoted diff) +6
-0+void *strbuf_start_filter(struct strbuf *sb, const char *src, ssize_t hint) +{ + if (src < sb->buf || src >= sb->buf + sb->alloc) { + if (hint > 0 && (size_t)hint >= sb->alloc) + strbuf_grow(sb, hint - sb->len); + return NULL;
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ } + + if (hint < 0) + return strbuf_detach(sb, NULL);what is that for? Why can't the caller just use
strbuf_detach? (He already has to pass negative hint somehow, which should be a concious action).
The idea is to have a unified API to deal with both the cases where
the filtering is known not to work in place by the caller, or for the cases where it could if enough space is allocated but that a realloc is needed.
Quoted 10 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ if ((size_t)hint >= sb->alloc) { + void *tmp = strbuf_detach(sb, NULL); + strbuf_grow(sb, hint); + return tmp; + } + + return NULL; +}How can one know when it sb is safe to use after
strbuf_end_filter?
We could document it, that's not an issue.
Quoted 3 lines
It is for the first "if", for example.
free()wont free the buf in sb. Oh, right, one can check if returned pointer !NULL. Which just adds more code to handle your API.
I don't get that part. free(NULL) is totally ok.
Quoted 2 lines
What actually happens to sb? Is it detached? Is it reallocated? When it is detached and when it is reallocated?
It is detached if the filter does not works in place (caller says that
with '-1' as a hint) or if it works in place but needs a buffer reallocation.
Quoted 2 lines
Why is the returned pointer useful only for giving it to
strbuf_end_filter?
Because the filter works on a {src, len} buffer, that points into the
buffer that we must free later in strbuf_end_filter.
Quoted 42 lines
Take for example your change in
crlf_to_git:convert.c +3
-3@@ -85,6 +85,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len, {struct text_stat stats;char *dst; +void *tmp;if ((action ==CRLF_BINARY ) || !auto_crlf || !len)return 0 ;@@ -110,9 +111,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,return 0 ; } -/* only grow if not in place */ -if (strbuf_avail (buf) + buf->len < len) -strbuf_grow (buf, len - buf->len); + tmp =strbuf_start_filter (buf, src, len); dst = buf->buf;if (action ==CRLF_GUESS ) {/* @@ -133,13 +132,14 @@ static int crlf_to_git(const char *path, const char *src, size_t len, }while (--len); }strbuf_setlen (buf, dst - buf->buf); +strbuf_end_filter (tmp);return 1 ; }And try to rewrite it with the
strbuf_make_room:convert.c +1
-3@@ -110,9 +111,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,return 0 ; } -/* only grow if not in place */ -if (strbuf_avail (buf) + buf->len < len) -strbuf_grow (buf, len - buf->len); +strbuf_make_room (buf, len); dst = buf->buf;if (action ==CRLF_GUESS ) {/*
Quoted 1 line
The change looks rather simple
but is wrong because you may just break "src". It's not the case here
because len always fits, but it's IMHO the kind of knowledge of the internals of strbufs we should not rely on.
Quoted 7 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+/*----- filter API -----*/ +extern void *strbuf_start_filter(struct strbuf *, const char *, ssize_t); +extern void strbuf_end_filter(void *p);I find the naming very confusing: what filtering takes place where? If
strbuf_end_filteris just free, why is it needed at all? For the sake of wrappingfree()?
Those are not filters, but help writing filters that would have this
prototype:
int some_filter(const char *src, size_t len, struct strbuf *dst);
Allowing [src..src+len[ to be a subpart of `dst'. The usual semantics
of such filters is that it returns 0 if it did nothing, 1 if it wrote in dst.
This way, you can write filter_all being filter1, filter2, filter3
done one after the other this way:
int filter_all(const char *src, size_t len, struct strbuf *dst) {
int res = 0;
res |= filter1(src, len, dst);
if (res) {
src = dst->buf;
len = dst->len;
}
res |= filter2(src, len, dst);
if (res) {
src = dst->buf;
len = dst->len;
}
return res | filter3(src, len, dst);
} Note that I did not created this semantics, it was how convert.c
worked already, in a even more convoluted way before.
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Pierre Habouzit, Sun, Oct 07, 2007 16:53:55 +0200:
Quoted 12 lines · Alex Riesen
Quoted 8 lines
If you continue to insist the code is generic enough to justify its residence in
strbuf.c, continue reading.First off, what was wrong with dumb
void strbuf_make_room(struct strbuf *, size_t newsize);again?
If newsize is >=
sb->allocthen the area is reallocated, the pointer
may move, and the "src" pointer would then be invalidated.
So what? You already _have_ to know it points inside the strbuf, so you can't expect it to be valid after any serious strbuf operation.
Quoted 8 lines
Quoted 3 lines
what is that for? Why can't the caller just use
strbuf_detach? (He already has to pass negative hint somehow, which should be a concious action).The idea is to have a unified API to deal with both the cases where
the filtering is known not to work in place by the caller, or for the cases where it could if enough space is allocated but that a realloc is needed.
this just makes it convoluted and opaque (as in "not transparent")
Quoted 11 lines
Quoted 10 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ if ((size_t)hint >= sb->alloc) { + void *tmp = strbuf_detach(sb, NULL); + strbuf_grow(sb, hint); + return tmp; + } + + return NULL; +}How can one know when it sb is safe to use after
strbuf_end_filter?
Quoted 1 line
We could document it, that's not an issue.
The fact that you _have_to_ document is.
Quoted 5 lines
Quoted 3 lines
It is for the first "if", for example.
free()wont free the buf in sb. Oh, right, one can check if returned pointer !NULL. Which just adds more code to handle your API.I don't get that part. free(NULL) is totally ok.
Not that. One have to store the result of start_filter and check it
Quoted 6 lines
Quoted 2 lines
What actually happens to sb? Is it detached? Is it reallocated? When it is detached and when it is reallocated?
It is detached if the filter does not works in place (caller says that
with '-1' as a hint) or if it works in place but needs a buffer reallocation.
Too many if's. Ugly
Quoted 2 lines
Note that I did not created this semantics, it was how
convert.c
worked already, in a even more convoluted way before.
And why shouldn't these semantics kept to convert.c?
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Quoted 9 lines · Alex Riesen
Pierre Habouzit, Sun, Oct 07, 2007 16:53:55 +0200:
Quoted 8 lines · Alex Riesen
Quoted 7 lines
If you continue to insist the code is generic enough to justify its residence in
strbuf.c, continue reading.First off, what was wrong with dumb
void strbuf_make_room(struct strbuf *, size_t newsize);
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
again?
If newsize is >=
sb->allocthen the area is reallocated, the pointer
may move, and the "src" pointer would then be invalidated.So what? You already _have_ to know it points inside the strbuf, so you can't expect it to be valid after any serious strbuf operation.
Then you can't write a filter, because you need to reallocate the
buffer before even starting to read the input buffer sometimes. If you reallocate the strbuf, and that your original buffer was in there, you lose.
Quoted 6 lines
Quoted 4 lines
The idea is to have a unified API to deal with both the cases where
the filtering is known not to work in place by the caller, or for the cases where it could if enough space is allocated but that a realloc is needed.this just makes it convoluted and opaque (as in "not transparent")
I'm totally open to better alternatives. We could probably easily get
rid of strbuf_end_filter, as whichever way to deal with the issue I try to solve in a better way, in the end, it will always be just a "free".
So, maybe there is a way to rename strbuf_start_filter so that it's
more straightforward. The way to use the API is:
@ char *to_free = NULL;
@ if ((src is inside dst && need_realloc) || operation is not in-place)
@ to_free = strbuf_detach(dst, NULL);
@ strbuf_make_room(dst, needed_size);
// do whatever you want with src and dst
free(to_free);strbuf_start_filter tries to implement the block marked with `@'. Of course:
* need_realloc == (needed_size >= dst->alloc)
* src is inside dst means:
src > dst->buf && src < dst->buf + dst->alloc
Though, those are both things that I find ugly to "know" in convert.c. How things are allocated in strbufs is one of the few things we don't want to assume anywhere outside of the strbuf module.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
It is for the first "if", for example.
free()wont free the buf in sb. Oh, right, one can check if returned pointer !NULL. Which just adds more code to handle your API.I don't get that part. free(NULL) is totally ok.
Not that. One have to store the result of
start_filterand check it
Why check it ? You don't have to check. You have to keep it until
you're done with "src". Whichever the result was. The return of strbuf_start_filter intends to stash a pointer to be freed for the cases where "src" points into the destination buffer.
Quoted 4 lines
Quoted 2 lines
Note that I did not created this semantics, it was how
convert.c
worked already, in a even more convoluted way before.And why shouldn't these semantics kept to
convert.c?
I missed where having this live in convert.c rather than in strbuf.c
makes it less ugly or better in any sense.
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Pierre Habouzit, Sun, Oct 07, 2007 18:52:18 +0200:
Quoted 7 lines
So, maybe there is a way to rename
strbuf_start_filterso that it's
more straightforward. The way to use the API is:@ char
*to_free= NULL;
@ if ((src is inside dst &&need_realloc) || operation is not in-place)
@to_free=strbuf_detach(dst, NULL);
@strbuf_make_room(dst, needed_size);
Why do you have to play these games with src being inside/outside? Don't you know where src already is? AFAICS in convert.c crlf_to_git - you do. And everywhere else you know this fact too.
Quoted 9 lines
// do whatever you want with src and dst free(to_free);
strbuf_start_filtertries to implement the block marked with `@'. Of course:
*need_realloc== (needed_size>=dst->alloc)
* src is inside dst means:src > dst->buf && src < dst->buf + dst->alloc
in convert.c it is never "inside". It is _exactly_ the ->buf. No need for generic "inside" check. Just test if src == ->buf.
And AFAICS, there is no other users of the function.
Quoted 3 lines
Though, those are both things that I find ugly to "know" in
convert.c. How things are allocated in strbufs is one of the few things we don't want to assume anywhere outside of the strbuf module.
src is outside of strbuf scope. It is not internal to struct strbuf. The caller must already know if it is inside of the given strbuf instance.
need_realloc is covered by make_room, isn't it?
I'd suggest just fix the caller, it is simple in convert.c: just use ret, which contains exactly this information. If you insist on editing in-place, which makes your routines really need the in-placeability informaion. Just give it to them, better explicitely. All of this makes the routines very convert.c specific, which is the reason why I argument to have them just there and nowhere else.
Alternatively, one can memdup ->buf (as it is the input for next filter) every time a filter modifies it (which is safe, but simple, slow, requires memory, and may fragment heap):
/* simplified */
int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst)
{
int crlf = CRLF_GUESS;
int ident = 0, ret = 0;
char *filter = NULL;
const char *tmp = src;
ret |= apply_filter(path, tmp, len, dst, filter);
if (ret) {
tmp = xmemdupz(dst->buf, dst->len);
len = dst->len;
}
if (crlf_to_git(path, tmp, len, dst, crlf)) {
ret |= 1;
if (tmp != src)
free((void*)tmp);
tmp = xmemdupz(dst->buf, dst->len);
len = dst->len;
}
ret |= ident_to_git(path, tmp, len, dst, ident);
if (tmp != src)
free((void*)tmp);
return ret;
}It is just to show that nothing like strbuf_start_filter is _needed_ at all.
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Quoted 10 lines · Alex Riesen
Pierre Habouzit, Sun, Oct 07, 2007 18:52:18 +0200:
Quoted 3 lines
Though, those are both things that I find ugly to "know" in
convert.c. How things are allocated in strbufs is one of the few things we don't want to assume anywhere outside of the strbuf module.src is outside of strbuf scope. It is not internal to struct strbuf. The caller must already know if it is inside of the given strbuf instance.
need_reallocis covered bymake_room, isn't it?
Internally yes, but make_room may move the buffer, if that happens,
there is nothing we can do, in the case where we point inside (or at the begining of - fwiw it's the same here) the buffer
Quoted 10 lines
I'd suggest just fix the caller, it is simple in
convert.c: just use ret, which contains exactly this information. If you insist on editing in-place, which makes your routines really need the in-placeability informaion. Just give it to them, better explicitely. All of this makes the routines veryconvert.cspecific, which is the reason why I argument to have them just there and nowhere else.Alternatively, one can memdup ->buf (as it is the input for next filter) every time a filter modifies it (which is safe, but simple, slow, requires memory, and may fragment heap):
This is exactly what we are trying to avoid with the current form.
Given how you try to micro-optimize strbuf_cmp I'm a bit lost here…
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [PATCH 1/2] Have a filter_start/filter_end API.
Pierre Habouzit, Mon, Oct 08, 2007 09:29:47 +0200:
Quoted 10 lines · Alex Riesen
Quoted 9 lines
Pierre Habouzit, Sun, Oct 07, 2007 18:52:18 +0200:
Quoted 3 lines
Though, those are both things that I find ugly to "know" in
convert.c. How things are allocated in strbufs is one of the few things we don't want to assume anywhere outside of the strbuf module.src is outside of strbuf scope. It is not internal to struct strbuf. The caller must already know if it is inside of the given strbuf instance.
Quoted 5 lines
Quoted 1 line
need_reallocis covered bymake_room, isn't it?Internally yes, but
make_roommay move the buffer, if that happens,
there is nothing we can do, in the case where we point inside (or at the begining of - fwiw it's the same here) the buffer
update the outside pointer you can. But I actually lost all interest:
personally I never use the filters and deeply despise the reasons caused their existence.
Quoted 7 lines
Quoted 7 lines
I'd suggest just fix the caller, it is simple in
convert.c: just use ret, which contains exactly this information. If you insist on editing in-place, which makes your routines really need the in-placeability informaion. Just give it to them, better explicitely. All of this makes the routines veryconvert.cspecific, which is the reason why I argument to have them just there and nowhere else.
Quoted 5 lines
Quoted 3 lines
Alternatively, one can memdup ->buf (as it is the input for next filter) every time a filter modifies it (which is safe, but simple, slow, requires memory, and may fragment heap):
This is exactly what we are trying to avoid with the current form.
I take this suggestion back. Do not use memdup, as it is slow, requires lots of memory and may fragment heap. Sorry for repeating.
Quoted 2 lines
Given how you try to micro-optimize
strbuf_cmpI'm a bit lost here…
I didn't. It just happened. If I _wanted_ to optimize anything I wouldn't start with a function which is used exactly one time in a program with a name like "rerere" which is not even used in default configuration.
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
In reply to Linus TorvaldsQuoted 14 lines · Linus Torvalds
Review
Quoted 5 lines · Pierre Habouzit
(quoted diff) +3
-1- strbuf_grow(buf, len); + /* only grow if not in place */ + if (strbuf_avail(buf) + buf->len < len) + strbuf_grow(buf, len - buf->len);Umm. This is really ugly.
The whole point of strbuf's was that you shouldn't be doing your own allocation decisions etc. So why do it?
Quoted 7 lines
Wouldn't it be much better to have a
strbuf_make_room()interface that just guarantees that there is enough room fo "len"?Otherwise, code like the above would seem to make the whole point of a safer string interface rather pointless. The above code only makes sense if you know how the strbuf's are internally done, so it should not exists except as internal strbuf code. No?
Took a short look at strbuf.h after seeing the above code.
And I was suprised to see that all strbuf users were exposed to the strbuf structure.
Following patch would at least make sure noone fiddle with strbuf internals. Cut'n'paste - only for the example of it.
It simply moves strbuf declaration to the .c file where it rightfully belongs.
git did not build with this change....
Sam
strbuf.c +8 -0
@@ -1,6 +1,14 @@#include "cache.h" #include "strbuf.h" +struct strbuf { +int alloc; +int len; +int eof; +char *buf; +}; + +void strbuf_init (struct strbuf *sb) { sb->buf =NULL ; sb->eof = sb->alloc = sb->len =0 ;
strbuf.h +1 -6
@@ -1,11 +1,6 @@#ifndef STRBUF_H #define STRBUF_H -struct strbuf { -int alloc; -int len; -int eof; -char *buf; -}; +struct strbuf ;extern void strbuf_init (struct strbuf *);extern void read_line (struct strbuf *,FILE *,int );
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 23 lines · Sam Ravnborg
Quoted 14 lines · Linus Torvalds
Review
Quoted 5 lines · Pierre Habouzit
(quoted diff) +3
-1- strbuf_grow(buf, len); + /* only grow if not in place */ + if (strbuf_avail(buf) + buf->len < len) + strbuf_grow(buf, len - buf->len);Umm. This is really ugly.
The whole point of strbuf's was that you shouldn't be doing your own allocation decisions etc. So why do it?
Quoted 7 lines
Wouldn't it be much better to have a
strbuf_make_room()interface that just guarantees that there is enough room fo "len"?Otherwise, code like the above would seem to make the whole point of a safer string interface rather pointless. The above code only makes sense if you know how the strbuf's are internally done, so it should not exists except as internal strbuf code. No?
Quoted 6 lines
Took a short look at
strbuf.hafter seeing the above code.
And I was suprised to see that all strbuf users were exposed to the strbuf structure.
Following patch would at least make sure noone fiddle with strbuf internals. Cut'n'paste - only for the example of it.
It simply moves strbuf declaration to the .c file where it rightfully belongs.
you're looking at an antiquated version, please look at the one in
current master on current next. In this one, what you can do or not do with the struct is explained
Quoted 1 line
git did not build with this change....
Of course it doesn't! people want to have direct access to ->buf and
->len, and it's definitely OK.
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 31 lines · Pierre Habouzit
Quoted 23 lines · Sam Ravnborg
Quoted 14 lines · Linus Torvalds
Review
Quoted 5 lines · Pierre Habouzit
(quoted diff) +3
-1- strbuf_grow(buf, len); + /* only grow if not in place */ + if (strbuf_avail(buf) + buf->len < len) + strbuf_grow(buf, len - buf->len);Umm. This is really ugly.
The whole point of strbuf's was that you shouldn't be doing your own allocation decisions etc. So why do it?
Quoted 7 lines
Wouldn't it be much better to have a
strbuf_make_room()interface that just guarantees that there is enough room fo "len"?Otherwise, code like the above would seem to make the whole point of a safer string interface rather pointless. The above code only makes sense if you know how the strbuf's are internally done, so it should not exists except as internal strbuf code. No?
Quoted 6 lines
Took a short look at
strbuf.hafter seeing the above code.
And I was suprised to see that all strbuf users were exposed to the strbuf structure.
Following patch would at least make sure noone fiddle with strbuf internals. Cut'n'paste - only for the example of it.
It simply moves strbuf declaration to the .c file where it rightfully belongs.
Quoted 8 lines
you're looking at an antiquated version, please look at the one in
current master on current next. In this one, what you can do or not do with the struct is explainedQuoted 1 line
git did not build with this change....
Of course it doesn't! people want to have direct access to ->buf and
->len, and it's definitely OK.
Understood now - thanks for the clarification.
Sam
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
In reply to Sam RavnborgQuoted 4 lines · Sam Ravnborg
Took a short look at
strbuf.hafter seeing the above code. And I was suprised to see that all strbuf users were exposed to the strbuf structure.
Well, they *have* to. We want people to declare their strbufs as automatic or static structures, and using a opaque struct pointer is *not* an option (like "FILE" is doing in stdio.h).
Quoted 1 line
Following patch would at least make sure noone fiddle with strbuf internals.
No, following patch is fundamentally broken - it's not even a good starting point. It's bad, bad, bad.
It's also broken in another way: we want it to be really easy to use strbuf's as normal C strings.
Yes, many (totally idiotic and broken) interfaces think it's so important to "protect" their internal data structures that you have a "string_to_c()" helper function for that. That may be "good abstraction", but it's totally idiotic, because it results in horrible source code!
Tell me which is more readable:
printf("Hello %s\n", sb->buf);or
printf("Hello %s\n", strbuf_to_c(sb));and I claim that anybody who claims that the latter is "more readable" is full of shit, and has an agenda to push, so it's "more agenda-friendly" rather than readable!
So having "sb->buf" and "sb->len" be visible to users is a *good* thing. Otherwise you end up having to create millions of idiotic small helper functions, rather than just use the standard ones.
Linus
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Hi Linus.
Quoted 11 lines
No, following patch is fundamentally broken - it's not even a good starting point. It's bad, bad, bad.
It's also broken in another way: we want it to be really easy to use strbuf's as normal C strings.
Yes, many (totally idiotic and broken) interfaces think it's so important to "protect" their internal data structures that you have a "
string_to_c()" helper function for that. That may be "good abstraction", but it's totally idiotic, because it results in horrible source code!
Quoted 7 lines
Tell me which is more readable:
printf("Hello %s\n", sb->buf);or
printf("Hello %s\n", strbuf_to_c(sb));
Point taken although no sane person would name it strbuf_to_c(...).
Sam
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 2 lines · Sam Ravnborg
Point taken although no sane person would name it
strbuf_to_c(...).
I agree with the "no sane person", but the problem is that the insane people seem to be in no short supply.
Go look at string libraries, and they all do something like that. Or worse.
- ustr: yes, it uses *exactly* what I described: "ustr_cstr()" and "ustr_len()" instead of having the data/length available easily (although it claims to do it for size reasons - perhaps valid in some cases!)
- libast: SPIF_CHARPTR_C(x). No, really.
- Vstr: doesn't have a linear data representation. Needs explicit flattening - although it appears to be something you're not ever supposed to do - it has 200+ functions to do various magic things.
- Qt (QString): QString::"data()", "ascii()" or "utf8()" or something.
At least this has the excuse of really being able to handle different locales (it didn't do that originally, though!), but they end up having a million helper functions exactly because you cannot use the normal string routines on anything!
- safesrtr, bstring: you just cast the pointer to "char *". Now *that* is classy and safe.
So there's a few sane out there, but I actually think they are in the minority (Glib, others)
Linus
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 6 lines · Linus Torvalds
- Qt (QString): QString::"data()", "
ascii()" or "utf8()" or something.At least this has the excuse of really being able to handle different locales (it didn't do that originally, though!), but they end up having a million helper functions exactly because you cannot use the normal string routines on anything!
I am afraid you cannot allow the direct access to the internal buffer of a string if this buffer can be implicitly shared between different instances as it is the case with QString. Because when you want to make some modification or want to get a non-const pointer to this buffer, its content has to be copied if the buffer is shared between a few copies.
On the other hand, I don't see what is the problem with using C string routines with it. ::data() returns a pointer and :capacity () returns allocated size of the buffer. ::resize() changes the size of the string. If you need a greater allocated size, you can use ::reserve(). Or did I miss something?
Dmitry
Re: [AGGREGATED PATCH] Fix in-place editing functions in convert.c
Quoted 6 lines · Dmitry Potapov
On the other hand, I don't see what is the problem with using C string routines with it. ::data() returns a pointer and :capacity () returns allocated size of the buffer. ::resize() changes the size of the string. If you need a greater allocated size, you can use ::reserve(). Or did I miss something?
No, you didn't miss anything. Except for the fact that the original point was that it's just a damn lot simpler and more straightforward to code just using "sb->buf" for the data. IOW, go back two or three emails in the thread.
Linus
Re: [PATCH] Fix in-place editing in crlf_to_git and ident_to_git.
In reply to Pierre HabouzitHi,
Quoted 3 lines · Pierre Habouzit
When
crlf_to_gitorident_to_gitare called "in place", the buffer already is big enough and should not be resized (as it could make the buffer address change, hence invalidate the `src' pointers !).
I wonder why we resize at all if the buffer is big enough to begin with.
Ciao,
Dscho
Re: [PATCH] Fix in-place editing in crlf_to_git and ident_to_git.
Quoted 9 lines · Johannes Schindelin
Hi,
Quoted 3 lines · Pierre Habouzit
When
crlf_to_gitorident_to_gitare called "in place", the buffer already is big enough and should not be resized (as it could make the buffer address change, hence invalidate the `src' pointers !).I wonder why we resize at all if the buffer is big enough to begin with.
strbuf_grow takes care of that itself but indeed you make me see that
my patch is wrong. if buf->len > len then len - buf->len is err a bit big.
I'll roll better ones.
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org