escape_utils 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +13 -0
  3. data/CHANGELOG.md +7 -0
  4. data/MIT-LICENSE +1 -1
  5. data/Rakefile +5 -18
  6. data/benchmark/html_escape.rb +9 -2
  7. data/benchmark/xml_escape.rb +29 -0
  8. data/escape_utils.gemspec +2 -3
  9. data/ext/escape_utils/buffer.c +181 -160
  10. data/ext/escape_utils/buffer.h +90 -68
  11. data/ext/escape_utils/escape_utils.c +77 -39
  12. data/ext/escape_utils/extconf.rb +1 -1
  13. data/ext/escape_utils/houdini.h +37 -8
  14. data/ext/escape_utils/houdini_href_e.c +115 -0
  15. data/ext/escape_utils/houdini_html_e.c +90 -0
  16. data/ext/escape_utils/houdini_html_u.c +122 -0
  17. data/ext/escape_utils/{houdini_js.c → houdini_js_e.c} +17 -75
  18. data/ext/escape_utils/houdini_js_u.c +60 -0
  19. data/ext/escape_utils/{uri_escape.h → houdini_uri_e.c} +68 -2
  20. data/ext/escape_utils/houdini_uri_u.c +65 -0
  21. data/ext/escape_utils/houdini_xml_e.c +136 -0
  22. data/lib/escape_utils/version.rb +1 -1
  23. data/lib/escape_utils/xml/builder.rb +8 -0
  24. data/test/helper.rb +14 -0
  25. data/test/html/escape_test.rb +61 -0
  26. data/test/html/unescape_test.rb +48 -0
  27. data/test/html_safety_test.rb +46 -0
  28. data/test/javascript/escape_test.rb +42 -0
  29. data/test/javascript/unescape_test.rb +46 -0
  30. data/test/query/escape_test.rb +50 -0
  31. data/test/query/unescape_test.rb +52 -0
  32. data/test/uri/escape_test.rb +50 -0
  33. data/test/uri/unescape_test.rb +55 -0
  34. data/test/url/escape_test.rb +58 -0
  35. data/test/url/unescape_test.rb +60 -0
  36. data/test/xml/escape_test.rb +67 -0
  37. metadata +136 -152
  38. data/.rspec +0 -2
  39. data/ext/escape_utils/houdini_html.c +0 -214
  40. data/ext/escape_utils/houdini_uri.c +0 -130
  41. data/spec/html/escape_spec.rb +0 -42
  42. data/spec/html/unescape_spec.rb +0 -37
  43. data/spec/html_safety_spec.rb +0 -48
  44. data/spec/javascript/escape_spec.rb +0 -34
  45. data/spec/javascript/unescape_spec.rb +0 -37
  46. data/spec/query/escape_spec.rb +0 -44
  47. data/spec/query/unescape_spec.rb +0 -46
  48. data/spec/rcov.opts +0 -3
  49. data/spec/spec_helper.rb +0 -5
  50. data/spec/uri/escape_spec.rb +0 -43
  51. data/spec/uri/unescape_spec.rb +0 -57
  52. data/spec/url/escape_spec.rb +0 -52
  53. data/spec/url/unescape_spec.rb +0 -57
data/.gitignore CHANGED
@@ -6,4 +6,5 @@ doc/*
6
6
  *.rbc
7
7
  tmp/
8
8
  Gemfile.lock
9
- vendor/*
9
+ vendor/*
10
+ bin/
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - ree
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: rbx-18mode
13
+ - rvm: rbx-19mode
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (February 26th, 2013)
4
+ * add xml escaping support
5
+ * in Ruby 1.9 - escape_utils now requires the input string be UTF-8
6
+ * update upstream houdini to pull in some speed optimizations
7
+ * a couple of other perf tweaks
8
+ * switched to minitest
9
+
3
10
  ## 0.2.4 (September 7th, 2011)
4
11
  * swap out custom escaping routines for houdini - https://github.com/tanoku/houdini
5
12
  * add RSTRING_NOT_MODIFIED define for a Rubinius speedup
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010-2011 Brian Lopez - http://github.com/brianmario
1
+ Copyright (c) 2010-2013 Brian Lopez - http://github.com/brianmario
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,23 +1,10 @@
1
- # rspec
2
- begin
3
- require 'rspec'
4
- require 'rspec/core/rake_task'
1
+ require 'rake/testtask'
5
2
 
6
- desc "Run all examples with RCov"
7
- RSpec::Core::RakeTask.new('spec:rcov') do |t|
8
- t.rcov = true
9
- end
10
- RSpec::Core::RakeTask.new('spec') do |t|
11
- t.verbose = true
12
- end
13
-
14
- task :default => :spec
15
- rescue LoadError
16
- puts "rspec, or one of its dependencies, is not available. Install it with: sudo gem install rspec"
3
+ Rake::TestTask.new do |t|
4
+ t.pattern = "test/**/*_test.rb"
17
5
  end
18
6
 
19
- # rake-compiler
20
- require 'rake' unless defined? Rake
7
+ task :default => :test
21
8
 
22
9
  gem 'rake-compiler', '>= 0.7.5'
23
10
  require "rake/extensiontask"
@@ -29,4 +16,4 @@ Rake::ExtensionTask.new('escape_utils') do |ext|
29
16
  ext.lib_dir = File.join 'lib', 'escape_utils'
30
17
  end
31
18
 
32
- Rake::Task[:spec].prerequisites << :compile
19
+ Rake::Task[:test].prerequisites << :compile
@@ -19,7 +19,7 @@ end
19
19
  times = 100
20
20
  url = "http://en.wikipedia.org/wiki/Line_of_succession_to_the_British_throne"
21
21
  html = `curl -s #{url}`
22
- html = html.force_encoding('binary') if html.respond_to?(:force_encoding)
22
+ html = html.force_encoding('utf-8') if html.respond_to?(:force_encoding)
23
23
  puts "Escaping #{html.bytesize} bytes of html #{times} times, from #{url}"
24
24
 
25
25
  Benchmark.bmbm do |x|
@@ -47,6 +47,13 @@ Benchmark.bmbm do |x|
47
47
  end
48
48
  end
49
49
 
50
+ x.report "String#gsub" do
51
+ html_escape = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
52
+ times.times do
53
+ html.gsub(/[&"'><]/, html_escape)
54
+ end
55
+ end
56
+
50
57
  x.report "fast_xs_extra#fast_xs_html" do
51
58
  times.times do
52
59
  html.fast_xs_html
@@ -58,4 +65,4 @@ Benchmark.bmbm do |x|
58
65
  EscapeUtils.escape_html(html)
59
66
  end
60
67
  end
61
- end
68
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'rubygems'
6
+ require 'benchmark'
7
+
8
+ require 'builder'
9
+ require 'escape_utils'
10
+
11
+ times = 100
12
+ url = "http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml"
13
+ xml = `curl -s #{url}`
14
+ xml = xml.force_encoding('binary') if xml.respond_to?(:force_encoding)
15
+ puts "Escaping #{xml.bytesize} bytes of xml #{times} times, from #{url}"
16
+
17
+ Benchmark.bmbm do |x|
18
+ x.report "Builder::String.to_xs" do
19
+ times.times do
20
+ xml.to_xs
21
+ end
22
+ end
23
+
24
+ x.report "EscapeUtils.escape_xml" do
25
+ times.times do
26
+ EscapeUtils.escape_xml(xml)
27
+ end
28
+ end
29
+ end
data/escape_utils.gemspec CHANGED
@@ -4,20 +4,19 @@ Gem::Specification.new do |s|
4
4
  s.name = %q{escape_utils}
5
5
  s.version = EscapeUtils::VERSION
6
6
  s.authors = ["Brian Lopez"]
7
- s.date = Time.now.utc.strftime("%Y-%m-%d")
8
7
  s.email = %q{seniorlopez@gmail.com}
9
8
  s.extensions = ["ext/escape_utils/extconf.rb"]
10
9
  s.files = `git ls-files`.split("\n")
11
10
  s.homepage = %q{http://github.com/brianmario/escape_utils}
12
11
  s.rdoc_options = ["--charset=UTF-8"]
13
- s.require_paths = ["lib", "ext"]
12
+ s.require_paths = ["lib"]
14
13
  s.rubygems_version = %q{1.4.2}
15
14
  s.summary = %q{Faster string escaping routines for your web apps}
16
15
  s.test_files = `git ls-files spec`.split("\n")
17
16
 
18
17
  # tests
19
18
  s.add_development_dependency 'rake-compiler', ">= 0.7.5"
20
- s.add_development_dependency 'rspec', ">= 2.0.0"
19
+ s.add_development_dependency 'minitest'
21
20
  # benchmarks
22
21
  s.add_development_dependency 'rack'
23
22
  s.add_development_dependency 'haml'
@@ -1,228 +1,249 @@
1
1
  /*
2
- * Copyright (c) 2008, Natacha Porté
3
- * Copyright (c) 2011, Vicent Martí
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
4
3
  *
5
- * Permission to use, copy, modify, and distribute this software for any
6
- * purpose with or without fee is hereby granted, provided that the above
7
- * copyright notice and this permission notice appear in all copies.
8
- *
9
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
16
6
  */
17
-
18
- #define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
19
-
20
- #include "buffer.h"
21
-
7
+ #include <stdarg.h>
8
+ #include <ctype.h>
9
+ #include <string.h>
10
+ #include <assert.h>
11
+ #include <string.h>
22
12
  #include <stdio.h>
23
13
  #include <stdlib.h>
24
- #include <string.h>
14
+ #include <sys/param.h>
25
15
 
26
- /* MSVC compat */
27
- #if defined(_MSC_VER)
28
- # define _buf_vsnprintf _vsnprintf
29
- #else
30
- # define _buf_vsnprintf vsnprintf
31
- #endif
16
+ #include "buffer.h"
32
17
 
33
- int
34
- bufprefix(const struct buf *buf, const char *prefix)
35
- {
36
- size_t i;
18
+ /* Used as default value for gh_buf->ptr so that people can always
19
+ * assume ptr is non-NULL and zero terminated even for new gh_bufs.
20
+ */
21
+ char gh_buf__initbuf[1];
22
+ char gh_buf__oom[1];
37
23
 
38
- for (i = 0; i < buf->size; ++i) {
39
- if (prefix[i] == 0)
40
- return 0;
24
+ #define ENSURE_SIZE(b, d) \
25
+ if ((d) > buf->asize && gh_buf_grow(b, (d)) < 0)\
26
+ return -1;
41
27
 
42
- if (buf->data[i] != prefix[i])
43
- return buf->data[i] - prefix[i];
44
- }
28
+ void gh_buf_init(gh_buf *buf, size_t initial_size)
29
+ {
30
+ buf->asize = 0;
31
+ buf->size = 0;
32
+ buf->ptr = gh_buf__initbuf;
45
33
 
46
- return 0;
34
+ if (initial_size)
35
+ gh_buf_grow(buf, initial_size);
47
36
  }
48
37
 
49
- /* bufgrow: increasing the allocated size to the given value */
50
- int
51
- bufgrow(struct buf *buf, size_t neosz)
38
+ int gh_buf_try_grow(gh_buf *buf, size_t target_size, bool mark_oom)
52
39
  {
53
- size_t neoasz;
54
- void *neodata;
55
- if (!buf || !buf->unit || neosz > BUFFER_MAX_ALLOC_SIZE)
56
- return BUF_ENOMEM;
40
+ char *new_ptr;
41
+ size_t new_size;
57
42
 
58
- if (buf->asize >= neosz)
59
- return BUF_OK;
43
+ if (buf->ptr == gh_buf__oom)
44
+ return -1;
60
45
 
61
- neoasz = buf->asize + buf->unit;
62
- while (neoasz < neosz)
63
- neoasz += buf->unit;
46
+ if (target_size <= buf->asize)
47
+ return 0;
64
48
 
65
- neodata = realloc(buf->data, neoasz);
66
- if (!neodata)
67
- return BUF_ENOMEM;
49
+ if (buf->asize == 0) {
50
+ new_size = target_size;
51
+ new_ptr = NULL;
52
+ } else {
53
+ new_size = buf->asize;
54
+ new_ptr = buf->ptr;
55
+ }
68
56
 
69
- buf->data = neodata;
70
- buf->asize = neoasz;
71
- return BUF_OK;
72
- }
57
+ /* grow the buffer size by 1.5, until it's big enough
58
+ * to fit our target size */
59
+ while (new_size < target_size)
60
+ new_size = (new_size << 1) - (new_size >> 1);
73
61
 
62
+ /* round allocation up to multiple of 8 */
63
+ new_size = (new_size + 7) & ~7;
74
64
 
75
- /* bufnew: allocation of a new buffer */
76
- struct buf *
77
- bufnew(size_t unit)
78
- {
79
- struct buf *ret;
80
- ret = malloc(sizeof (struct buf));
65
+ new_ptr = realloc(new_ptr, new_size);
81
66
 
82
- if (ret) {
83
- ret->data = 0;
84
- ret->size = ret->asize = 0;
85
- ret->unit = unit;
67
+ if (!new_ptr) {
68
+ if (mark_oom)
69
+ buf->ptr = gh_buf__oom;
70
+ return -1;
86
71
  }
87
- return ret;
72
+
73
+ buf->asize = new_size;
74
+ buf->ptr = new_ptr;
75
+
76
+ /* truncate the existing buffer size if necessary */
77
+ if (buf->size >= buf->asize)
78
+ buf->size = buf->asize - 1;
79
+ buf->ptr[buf->size] = '\0';
80
+
81
+ return 0;
88
82
  }
89
83
 
90
- /* bufnullterm: NULL-termination of the string array */
91
- const char *
92
- bufcstr(struct buf *buf)
84
+ void gh_buf_free(gh_buf *buf)
93
85
  {
94
- if (!buf || !buf->unit)
95
- return NULL;
86
+ if (!buf) return;
96
87
 
97
- if (buf->size < buf->asize && buf->data[buf->size] == 0)
98
- return (char *)buf->data;
88
+ if (buf->ptr != gh_buf__initbuf && buf->ptr != gh_buf__oom)
89
+ free(buf->ptr);
99
90
 
100
- if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {
101
- buf->data[buf->size] = 0;
102
- return (char *)buf->data;
103
- }
104
-
105
- return NULL;
91
+ gh_buf_init(buf, 0);
106
92
  }
107
93
 
108
- /* bufprintf: formatted printing to a buffer */
109
- void
110
- bufprintf(struct buf *buf, const char *fmt, ...)
94
+ void gh_buf_clear(gh_buf *buf)
111
95
  {
112
- va_list ap;
113
- if (!buf || !buf->unit)
114
- return;
96
+ buf->size = 0;
97
+ if (buf->asize > 0)
98
+ buf->ptr[0] = '\0';
99
+ }
115
100
 
116
- va_start(ap, fmt);
117
- vbufprintf(buf, fmt, ap);
118
- va_end(ap);
101
+ int gh_buf_set(gh_buf *buf, const char *data, size_t len)
102
+ {
103
+ if (len == 0 || data == NULL) {
104
+ gh_buf_clear(buf);
105
+ } else {
106
+ if (data != buf->ptr) {
107
+ ENSURE_SIZE(buf, len + 1);
108
+ memmove(buf->ptr, data, len);
109
+ }
110
+ buf->size = len;
111
+ buf->ptr[buf->size] = '\0';
112
+ }
113
+ return 0;
119
114
  }
120
115
 
121
- /* bufput: appends raw data to a buffer */
122
- void
123
- bufput(struct buf *buf, const void *data, size_t len)
116
+ int gh_buf_sets(gh_buf *buf, const char *string)
124
117
  {
125
- if (!buf)
126
- return;
118
+ return gh_buf_set(buf, string, string ? strlen(string) : 0);
119
+ }
127
120
 
128
- if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)
129
- return;
121
+ int gh_buf_putc(gh_buf *buf, char c)
122
+ {
123
+ ENSURE_SIZE(buf, buf->size + 2);
124
+ buf->ptr[buf->size++] = c;
125
+ buf->ptr[buf->size] = '\0';
126
+ return 0;
127
+ }
130
128
 
131
- memcpy(buf->data + buf->size, data, len);
129
+ int gh_buf_put(gh_buf *buf, const void *data, size_t len)
130
+ {
131
+ ENSURE_SIZE(buf, buf->size + len + 1);
132
+ memmove(buf->ptr + buf->size, data, len);
132
133
  buf->size += len;
134
+ buf->ptr[buf->size] = '\0';
135
+ return 0;
133
136
  }
134
137
 
135
- /* bufputs: appends a NUL-terminated string to a buffer */
136
- void
137
- bufputs(struct buf *buf, const char *str)
138
+ int gh_buf_puts(gh_buf *buf, const char *string)
138
139
  {
139
- bufput(buf, str, strlen(str));
140
+ assert(string);
141
+ return gh_buf_put(buf, string, strlen(string));
140
142
  }
141
143
 
142
-
143
- /* bufputc: appends a single uint8_t to a buffer */
144
- void
145
- bufputc(struct buf *buf, int c)
144
+ int gh_buf_vprintf(gh_buf *buf, const char *format, va_list ap)
146
145
  {
147
- if (!buf)
148
- return;
146
+ int len;
147
+ const size_t expected_size = buf->size + (strlen(format) * 2);
149
148
 
150
- if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)
151
- return;
149
+ ENSURE_SIZE(buf, expected_size);
152
150
 
153
- buf->data[buf->size] = c;
154
- buf->size += 1;
155
- }
151
+ while (1) {
152
+ va_list args;
153
+ va_copy(args, ap);
156
154
 
157
- /* bufrelease: decrease the reference count and free the buffer if needed */
158
- void
159
- bufrelease(struct buf *buf)
160
- {
161
- if (!buf)
162
- return;
155
+ len = vsnprintf(
156
+ buf->ptr + buf->size,
157
+ buf->asize - buf->size,
158
+ format, args
159
+ );
163
160
 
164
- free(buf->data);
165
- free(buf);
166
- }
161
+ if (len < 0) {
162
+ free(buf->ptr);
163
+ buf->ptr = gh_buf__oom;
164
+ return -1;
165
+ }
166
+
167
+ if ((size_t)len + 1 <= buf->asize - buf->size) {
168
+ buf->size += len;
169
+ break;
170
+ }
167
171
 
172
+ ENSURE_SIZE(buf, buf->size + len + 1);
173
+ }
168
174
 
169
- /* bufreset: frees internal data of the buffer */
170
- void
171
- bufreset(struct buf *buf)
175
+ return 0;
176
+ }
177
+
178
+ int gh_buf_printf(gh_buf *buf, const char *format, ...)
172
179
  {
173
- if (!buf)
174
- return;
180
+ int r;
181
+ va_list ap;
175
182
 
176
- free(buf->data);
177
- buf->data = NULL;
178
- buf->size = buf->asize = 0;
183
+ va_start(ap, format);
184
+ r = gh_buf_vprintf(buf, format, ap);
185
+ va_end(ap);
186
+
187
+ return r;
179
188
  }
180
189
 
181
- /* bufslurp: removes a given number of bytes from the head of the array */
182
- void
183
- bufslurp(struct buf *buf, size_t len)
190
+ void gh_buf_copy_cstr(char *data, size_t datasize, const gh_buf *buf)
184
191
  {
185
- if (!buf || !buf->unit || len <= 0)
186
- return;
192
+ size_t copylen;
193
+
194
+ assert(data && datasize && buf);
187
195
 
188
- if (len >= buf->size) {
189
- buf->size = 0;
196
+ data[0] = '\0';
197
+
198
+ if (buf->size == 0 || buf->asize <= 0)
190
199
  return;
191
- }
192
200
 
193
- buf->size -= len;
194
- memmove(buf->data, buf->data + len, buf->size);
201
+ copylen = buf->size;
202
+ if (copylen > datasize - 1)
203
+ copylen = datasize - 1;
204
+ memmove(data, buf->ptr, copylen);
205
+ data[copylen] = '\0';
195
206
  }
196
207
 
197
- /* vbufprintf: stdarg variant of formatted printing into a buffer */
198
- void
199
- vbufprintf(struct buf *buf, const char *fmt, va_list ap)
208
+ void gh_buf_swap(gh_buf *buf_a, gh_buf *buf_b)
200
209
  {
201
- int n;
210
+ gh_buf t = *buf_a;
211
+ *buf_a = *buf_b;
212
+ *buf_b = t;
213
+ }
202
214
 
203
- if (buf == 0 || (buf->size >= buf->asize && bufgrow(buf, buf->size + 1)) < 0)
204
- return;
215
+ char *gh_buf_detach(gh_buf *buf)
216
+ {
217
+ char *data = buf->ptr;
205
218
 
206
- n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
219
+ if (buf->asize == 0 || buf->ptr == gh_buf__oom)
220
+ return NULL;
207
221
 
208
- if (n < 0) {
209
- #ifdef _MSC_VER
210
- n = _vscprintf(fmt, ap);
211
- #else
212
- return;
213
- #endif
214
- }
222
+ gh_buf_init(buf, 0);
215
223
 
216
- if ((size_t)n >= buf->asize - buf->size) {
217
- if (bufgrow(buf, buf->size + n + 1) < 0)
218
- return;
224
+ return data;
225
+ }
219
226
 
220
- n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
227
+ void gh_buf_attach(gh_buf *buf, char *ptr, size_t asize)
228
+ {
229
+ gh_buf_free(buf);
230
+
231
+ if (ptr) {
232
+ buf->ptr = ptr;
233
+ buf->size = strlen(ptr);
234
+ if (asize)
235
+ buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
236
+ else /* pass 0 to fall back on strlen + 1 */
237
+ buf->asize = buf->size + 1;
238
+ } else {
239
+ gh_buf_grow(buf, asize);
221
240
  }
241
+ }
222
242
 
223
- if (n < 0)
224
- return;
225
-
226
- buf->size += n;
243
+ int gh_buf_cmp(const gh_buf *a, const gh_buf *b)
244
+ {
245
+ int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
246
+ return (result != 0) ? result :
247
+ (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
227
248
  }
228
249