2 * string.c: routines to manipulate counted-length strings
3 * (svn_stringbuf_t and svn_string_t) and C strings.
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
23 * ====================================================================
30 #include <string.h> /* for memcpy(), memcmp(), strlen() */
31 #include <apr_fnmatch.h>
32 #include "svn_string.h" /* loads "svn_types.h" and <apr_pools.h> */
33 #include "svn_ctype.h"
34 #include "private/svn_dep_compat.h"
35 #include "private/svn_string_private.h"
37 #include "svn_private_config.h"
41 /* Allocate the space for a memory buffer from POOL.
42 * Return a pointer to the new buffer in *DATA and its size in *SIZE.
43 * The buffer size will be at least MINIMUM_SIZE.
45 * N.B.: The stringbuf creation functions use this, but since stringbufs
46 * always consume at least 1 byte for the NUL terminator, the
47 * resulting data pointers will never be NULL.
49 static APR_INLINE
void
50 membuf_create(void **data
, apr_size_t
*size
,
51 apr_size_t minimum_size
, apr_pool_t
*pool
)
53 /* apr_palloc will allocate multiples of 8.
54 * Thus, we would waste some of that memory if we stuck to the
55 * smaller size. Note that this is safe even if apr_palloc would
56 * use some other aligment or none at all. */
57 minimum_size
= APR_ALIGN_DEFAULT(minimum_size
);
58 *data
= (!minimum_size
? NULL
: apr_palloc(pool
, minimum_size
));
62 /* Ensure that the size of a given memory buffer is at least MINIMUM_SIZE
63 * bytes. If *SIZE is already greater than or equal to MINIMUM_SIZE,
64 * this function does nothing.
66 * If *SIZE is 0, the allocated buffer size will be MINIMUM_SIZE
67 * rounded up to the nearest APR alignment boundary. Otherwse, *SIZE
68 * will be multiplied by a power of two such that the result is
69 * greater or equal to MINIMUM_SIZE. The pointer to the new buffer
70 * will be returned in *DATA, and its size in *SIZE.
72 static APR_INLINE
void
73 membuf_ensure(void **data
, apr_size_t
*size
,
74 apr_size_t minimum_size
, apr_pool_t
*pool
)
76 if (minimum_size
> *size
)
78 apr_size_t new_size
= *size
;
81 /* APR will increase odd allocation sizes to the next
82 * multiple for 8, for instance. Take advantage of that
83 * knowledge and allow for the extra size to be used. */
84 new_size
= minimum_size
;
86 while (new_size
< minimum_size
)
88 /* new_size is aligned; doubling it should keep it aligned */
89 const apr_size_t prev_size
= new_size
;
92 /* check for apr_size_t overflow */
93 if (prev_size
> new_size
)
95 new_size
= minimum_size
;
100 membuf_create(data
, size
, new_size
, pool
);
105 svn_membuf__create(svn_membuf_t
*membuf
, apr_size_t size
, apr_pool_t
*pool
)
107 membuf_create(&membuf
->data
, &membuf
->size
, size
, pool
);
112 svn_membuf__ensure(svn_membuf_t
*membuf
, apr_size_t size
)
114 membuf_ensure(&membuf
->data
, &membuf
->size
, size
, membuf
->pool
);
118 svn_membuf__resize(svn_membuf_t
*membuf
, apr_size_t size
)
120 const void *const old_data
= membuf
->data
;
121 const apr_size_t old_size
= membuf
->size
;
123 membuf_ensure(&membuf
->data
, &membuf
->size
, size
, membuf
->pool
);
124 if (membuf
->data
&& old_data
&& old_data
!= membuf
->data
)
125 memcpy(membuf
->data
, old_data
, old_size
);
128 /* Always provide an out-of-line implementation of svn_membuf__zero */
129 #undef svn_membuf__zero
131 svn_membuf__zero(svn_membuf_t
*membuf
)
133 SVN_MEMBUF__ZERO(membuf
);
136 /* Always provide an out-of-line implementation of svn_membuf__nzero */
137 #undef svn_membuf__nzero
139 svn_membuf__nzero(svn_membuf_t
*membuf
, apr_size_t size
)
141 SVN_MEMBUF__NZERO(membuf
, size
);
144 static APR_INLINE svn_boolean_t
145 string_compare(const char *str1
,
150 /* easy way out :) */
154 /* now the strings must have identical lenghths */
156 if ((memcmp(str1
, str2
, len1
)) == 0)
162 static APR_INLINE apr_size_t
163 string_first_non_whitespace(const char *str
, apr_size_t len
)
167 for (i
= 0; i
< len
; i
++)
169 if (! svn_ctype_isspace(str
[i
]))
173 /* if we get here, then the string must be entirely whitespace */
177 static APR_INLINE apr_size_t
178 find_char_backward(const char *str
, apr_size_t len
, char ch
)
188 /* char was not found, return len */
193 /* svn_string functions */
195 /* Return a new svn_string_t object, allocated in POOL, initialized with
196 * DATA and SIZE. Do not copy the contents of DATA, just store the pointer.
197 * SIZE is the length in bytes of DATA, excluding the required NUL
199 static svn_string_t
*
200 create_string(const char *data
, apr_size_t size
,
203 svn_string_t
*new_string
;
205 new_string
= apr_palloc(pool
, sizeof(*new_string
));
207 new_string
->data
= data
;
208 new_string
->len
= size
;
213 /* A data buffer for a zero-length string (just a null terminator). Many
214 * svn_string_t instances may share this same buffer. */
215 static const char empty_buffer
[1] = {0};
218 svn_string_create_empty(apr_pool_t
*pool
)
220 svn_string_t
*new_string
= apr_palloc(pool
, sizeof(*new_string
));
221 new_string
->data
= empty_buffer
;
229 svn_string_ncreate(const char *bytes
, apr_size_t size
, apr_pool_t
*pool
)
233 svn_string_t
*new_string
;
235 /* Allocate memory for svn_string_t and data in one chunk. */
236 mem
= apr_palloc(pool
, sizeof(*new_string
) + size
+ 1);
237 data
= (char*)mem
+ sizeof(*new_string
);
240 new_string
->data
= data
;
241 new_string
->len
= size
;
243 memcpy(data
, bytes
, size
);
245 /* Null termination is the convention -- even if we suspect the data
246 to be binary, it's not up to us to decide, it's the caller's
247 call. Heck, that's why they call it the caller! */
255 svn_string_create(const char *cstring
, apr_pool_t
*pool
)
257 return svn_string_ncreate(cstring
, strlen(cstring
), pool
);
262 svn_string_create_from_buf(const svn_stringbuf_t
*strbuf
, apr_pool_t
*pool
)
264 return svn_string_ncreate(strbuf
->data
, strbuf
->len
, pool
);
269 svn_string_createv(apr_pool_t
*pool
, const char *fmt
, va_list ap
)
271 char *data
= apr_pvsprintf(pool
, fmt
, ap
);
273 /* wrap an svn_string_t around the new data */
274 return create_string(data
, strlen(data
), pool
);
279 svn_string_createf(apr_pool_t
*pool
, const char *fmt
, ...)
285 str
= svn_string_createv(pool
, fmt
, ap
);
293 svn_string_isempty(const svn_string_t
*str
)
295 return (str
->len
== 0);
300 svn_string_dup(const svn_string_t
*original_string
, apr_pool_t
*pool
)
302 return (svn_string_ncreate(original_string
->data
,
303 original_string
->len
, pool
));
309 svn_string_compare(const svn_string_t
*str1
, const svn_string_t
*str2
)
312 string_compare(str1
->data
, str2
->data
, str1
->len
, str2
->len
);
318 svn_string_first_non_whitespace(const svn_string_t
*str
)
321 string_first_non_whitespace(str
->data
, str
->len
);
326 svn_string_find_char_backward(const svn_string_t
*str
, char ch
)
328 return find_char_backward(str
->data
, str
->len
, ch
);
332 svn_stringbuf__morph_into_string(svn_stringbuf_t
*strbuf
)
334 /* In debug mode, detect attempts to modify the original STRBUF object.
338 strbuf
->blocksize
= strbuf
->len
+ 1;
341 /* Both, svn_string_t and svn_stringbuf_t are public API structures
342 * since the svn epoch. Thus, we can rely on their precise layout not
345 * It just so happens that svn_string_t is structurally equivalent
346 * to the (data, len) sub-set of svn_stringbuf_t. There is also no
347 * difference in alignment and padding. So, we can just re-interpret
348 * that part of STRBUF as a svn_string_t.
350 * However, since svn_string_t does not know about the blocksize
351 * member in svn_stringbuf_t, any attempt to re-size the returned
352 * svn_string_t might invalidate the STRBUF struct. Hence, we consider
353 * the source STRBUF "consumed".
355 * Modifying the string character content is fine, though.
357 return (svn_string_t
*)&strbuf
->data
;
362 /* svn_stringbuf functions */
365 svn_stringbuf_create_empty(apr_pool_t
*pool
)
367 return svn_stringbuf_create_ensure(0, pool
);
371 svn_stringbuf_create_ensure(apr_size_t blocksize
, apr_pool_t
*pool
)
374 svn_stringbuf_t
*new_string
;
376 ++blocksize
; /* + space for '\0' */
378 /* Allocate memory for svn_string_t and data in one chunk. */
379 membuf_create(&mem
, &blocksize
, blocksize
+ sizeof(*new_string
), pool
);
381 /* Initialize header and string */
383 new_string
->data
= (char*)mem
+ sizeof(*new_string
);
384 new_string
->data
[0] = '\0';
386 new_string
->blocksize
= blocksize
- sizeof(*new_string
);
387 new_string
->pool
= pool
;
393 svn_stringbuf_ncreate(const char *bytes
, apr_size_t size
, apr_pool_t
*pool
)
395 svn_stringbuf_t
*strbuf
= svn_stringbuf_create_ensure(size
, pool
);
396 memcpy(strbuf
->data
, bytes
, size
);
398 /* Null termination is the convention -- even if we suspect the data
399 to be binary, it's not up to us to decide, it's the caller's
400 call. Heck, that's why they call it the caller! */
401 strbuf
->data
[size
] = '\0';
409 svn_stringbuf_create(const char *cstring
, apr_pool_t
*pool
)
411 return svn_stringbuf_ncreate(cstring
, strlen(cstring
), pool
);
416 svn_stringbuf_create_from_string(const svn_string_t
*str
, apr_pool_t
*pool
)
418 return svn_stringbuf_ncreate(str
->data
, str
->len
, pool
);
423 svn_stringbuf_createv(apr_pool_t
*pool
, const char *fmt
, va_list ap
)
425 char *data
= apr_pvsprintf(pool
, fmt
, ap
);
426 apr_size_t size
= strlen(data
);
427 svn_stringbuf_t
*new_string
;
429 new_string
= apr_palloc(pool
, sizeof(*new_string
));
430 new_string
->data
= data
;
431 new_string
->len
= size
;
432 new_string
->blocksize
= size
+ 1;
433 new_string
->pool
= pool
;
440 svn_stringbuf_createf(apr_pool_t
*pool
, const char *fmt
, ...)
442 svn_stringbuf_t
*str
;
446 str
= svn_stringbuf_createv(pool
, fmt
, ap
);
454 svn_stringbuf_fillchar(svn_stringbuf_t
*str
, unsigned char c
)
456 memset(str
->data
, c
, str
->len
);
461 svn_stringbuf_set(svn_stringbuf_t
*str
, const char *value
)
463 apr_size_t amt
= strlen(value
);
465 svn_stringbuf_ensure(str
, amt
);
466 memcpy(str
->data
, value
, amt
+ 1);
471 svn_stringbuf_setempty(svn_stringbuf_t
*str
)
481 svn_stringbuf_chop(svn_stringbuf_t
*str
, apr_size_t nbytes
)
483 if (nbytes
> str
->len
)
488 str
->data
[str
->len
] = '\0';
493 svn_stringbuf_isempty(const svn_stringbuf_t
*str
)
495 return (str
->len
== 0);
500 svn_stringbuf_ensure(svn_stringbuf_t
*str
, apr_size_t minimum_size
)
503 ++minimum_size
; /* + space for '\0' */
505 membuf_ensure(&mem
, &str
->blocksize
, minimum_size
, str
->pool
);
506 if (mem
&& mem
!= str
->data
)
509 memcpy(mem
, str
->data
, str
->len
+ 1);
515 /* WARNING - Optimized code ahead!
516 * This function has been hand-tuned for performance. Please read
517 * the comments below before modifying the code.
520 svn_stringbuf_appendbyte(svn_stringbuf_t
*str
, char byte
)
523 apr_size_t old_len
= str
->len
;
525 /* In most cases, there will be pre-allocated memory left
526 * to just write the new byte at the end of the used section
527 * and terminate the string properly.
529 if (str
->blocksize
> old_len
+ 1)
531 /* The following read does not depend this write, so we
532 * can issue the write first to minimize register pressure:
533 * The value of old_len+1 is no longer needed; on most processors,
534 * dest[old_len+1] will be calculated implicitly as part of
535 * the addressing scheme.
537 str
->len
= old_len
+1;
539 /* Since the compiler cannot be sure that *src->data and *src
540 * don't overlap, we read src->data *once* before writing
541 * to *src->data. Replacing dest with str->data would force
542 * the compiler to read it again after the first byte.
546 /* If not already available in a register as per ABI, load
547 * "byte" into the register (e.g. the one freed from old_len+1),
548 * then write it to the string buffer and terminate it properly.
550 * Including the "byte" fetch, all operations so far could be
551 * issued at once and be scheduled at the CPU's descression.
552 * Most likely, no-one will soon depend on the data that will be
553 * written in this function. So, no stalls there, either.
555 dest
[old_len
] = byte
;
556 dest
[old_len
+1] = '\0';
560 /* we need to re-allocate the string buffer
561 * -> let the more generic implementation take care of that part
564 /* Depending on the ABI, "byte" is a register value. If we were
565 * to take its address directly, the compiler might decide to
566 * put in on the stack *unconditionally*, even if that would
567 * only be necessary for this block.
570 svn_stringbuf_appendbytes(str
, &b
, 1);
576 svn_stringbuf_appendbytes(svn_stringbuf_t
*str
, const char *bytes
,
579 apr_size_t total_len
;
582 total_len
= str
->len
+ count
; /* total size needed */
584 /* svn_stringbuf_ensure adds 1 for null terminator. */
585 svn_stringbuf_ensure(str
, total_len
);
587 /* get address 1 byte beyond end of original bytestring */
588 start_address
= (str
->data
+ str
->len
);
590 memcpy(start_address
, bytes
, count
);
591 str
->len
= total_len
;
593 str
->data
[str
->len
] = '\0'; /* We don't know if this is binary
594 data or not, but convention is
595 to null-terminate. */
600 svn_stringbuf_appendstr(svn_stringbuf_t
*targetstr
,
601 const svn_stringbuf_t
*appendstr
)
603 svn_stringbuf_appendbytes(targetstr
, appendstr
->data
, appendstr
->len
);
608 svn_stringbuf_appendcstr(svn_stringbuf_t
*targetstr
, const char *cstr
)
610 svn_stringbuf_appendbytes(targetstr
, cstr
, strlen(cstr
));
614 svn_stringbuf_insert(svn_stringbuf_t
*str
,
619 if (bytes
+ count
> str
->data
&& bytes
< str
->data
+ str
->blocksize
)
621 /* special case: BYTES overlaps with this string -> copy the source */
622 const char *temp
= apr_pstrndup(str
->pool
, bytes
, count
);
623 svn_stringbuf_insert(str
, pos
, temp
, count
);
630 svn_stringbuf_ensure(str
, str
->len
+ count
);
631 memmove(str
->data
+ pos
+ count
, str
->data
+ pos
, str
->len
- pos
+ 1);
632 memcpy(str
->data
+ pos
, bytes
, count
);
639 svn_stringbuf_remove(svn_stringbuf_t
*str
,
645 if (pos
+ count
> str
->len
)
646 count
= str
->len
- pos
;
648 memmove(str
->data
+ pos
, str
->data
+ pos
+ count
, str
->len
- pos
- count
+ 1);
653 svn_stringbuf_replace(svn_stringbuf_t
*str
,
655 apr_size_t old_count
,
657 apr_size_t new_count
)
659 if (bytes
+ new_count
> str
->data
&& bytes
< str
->data
+ str
->blocksize
)
661 /* special case: BYTES overlaps with this string -> copy the source */
662 const char *temp
= apr_pstrndup(str
->pool
, bytes
, new_count
);
663 svn_stringbuf_replace(str
, pos
, old_count
, temp
, new_count
);
669 if (pos
+ old_count
> str
->len
)
670 old_count
= str
->len
- pos
;
672 if (old_count
< new_count
)
674 apr_size_t delta
= new_count
- old_count
;
675 svn_stringbuf_ensure(str
, str
->len
+ delta
);
678 if (old_count
!= new_count
)
679 memmove(str
->data
+ pos
+ new_count
, str
->data
+ pos
+ old_count
,
680 str
->len
- pos
- old_count
+ 1);
682 memcpy(str
->data
+ pos
, bytes
, new_count
);
683 str
->len
+= new_count
- old_count
;
689 svn_stringbuf_dup(const svn_stringbuf_t
*original_string
, apr_pool_t
*pool
)
691 return (svn_stringbuf_ncreate(original_string
->data
,
692 original_string
->len
, pool
));
698 svn_stringbuf_compare(const svn_stringbuf_t
*str1
,
699 const svn_stringbuf_t
*str2
)
701 return string_compare(str1
->data
, str2
->data
, str1
->len
, str2
->len
);
707 svn_stringbuf_first_non_whitespace(const svn_stringbuf_t
*str
)
709 return string_first_non_whitespace(str
->data
, str
->len
);
714 svn_stringbuf_strip_whitespace(svn_stringbuf_t
*str
)
716 /* Find first non-whitespace character */
717 apr_size_t offset
= svn_stringbuf_first_non_whitespace(str
);
719 /* Go ahead! Waste some RAM, we've got pools! :) */
722 str
->blocksize
-= offset
;
724 /* Now that we've trimmed the front, trim the end, wasting more RAM. */
725 while ((str
->len
> 0) && svn_ctype_isspace(str
->data
[str
->len
- 1]))
727 str
->data
[str
->len
] = '\0';
732 svn_stringbuf_find_char_backward(const svn_stringbuf_t
*str
, char ch
)
734 return find_char_backward(str
->data
, str
->len
, ch
);
739 svn_string_compare_stringbuf(const svn_string_t
*str1
,
740 const svn_stringbuf_t
*str2
)
742 return string_compare(str1
->data
, str2
->data
, str1
->len
, str2
->len
);
747 /*** C string stuff. ***/
750 svn_cstring_split_append(apr_array_header_t
*array
,
752 const char *sep_chars
,
753 svn_boolean_t chop_whitespace
,
759 pats
= apr_pstrdup(pool
, input
); /* strtok wants non-const data */
760 p
= svn_cstring_tokenize(sep_chars
, &pats
);
766 while (svn_ctype_isspace(*p
))
770 char *e
= p
+ (strlen(p
) - 1);
771 while ((e
>= p
) && (svn_ctype_isspace(*e
)))
778 APR_ARRAY_PUSH(array
, const char *) = p
;
780 p
= svn_cstring_tokenize(sep_chars
, &pats
);
788 svn_cstring_split(const char *input
,
789 const char *sep_chars
,
790 svn_boolean_t chop_whitespace
,
793 apr_array_header_t
*a
= apr_array_make(pool
, 5, sizeof(input
));
794 svn_cstring_split_append(a
, input
, sep_chars
, chop_whitespace
, pool
);
799 svn_boolean_t
svn_cstring_match_glob_list(const char *str
,
800 const apr_array_header_t
*list
)
804 for (i
= 0; i
< list
->nelts
; i
++)
806 const char *this_pattern
= APR_ARRAY_IDX(list
, i
, char *);
808 if (apr_fnmatch(this_pattern
, str
, 0) == APR_SUCCESS
)
816 svn_cstring_match_list(const char *str
, const apr_array_header_t
*list
)
820 for (i
= 0; i
< list
->nelts
; i
++)
822 const char *this_str
= APR_ARRAY_IDX(list
, i
, char *);
824 if (strcmp(this_str
, str
) == 0)
832 svn_cstring_tokenize(const char *sep
, char **str
)
838 /* check parameters */
839 if ((sep
== NULL
) || (str
== NULL
) || (*str
== NULL
))
842 /* let APR handle edge cases and multiple separators */
844 if (csep
== '\0' || sep
[1] != '\0')
845 return apr_strtok(NULL
, sep
, str
);
847 /* skip characters in sep (will terminate at '\0') */
849 while (*token
== csep
)
852 if (!*token
) /* no more tokens */
855 /* skip valid token characters to terminate token and
856 * prepare for the next call (will terminate at '\0)
858 next
= strchr(token
, csep
);
861 *str
= token
+ strlen(token
);
865 *(char *)next
= '\0';
866 *str
= (char *)next
+ 1;
872 int svn_cstring_count_newlines(const char *msg
)
877 for (p
= msg
; *p
; p
++)
882 if (*(p
+ 1) == '\r')
888 if (*(p
+ 1) == '\n')
897 svn_cstring_join(const apr_array_header_t
*strings
,
898 const char *separator
,
901 svn_stringbuf_t
*new_str
= svn_stringbuf_create_empty(pool
);
902 size_t sep_len
= strlen(separator
);
905 for (i
= 0; i
< strings
->nelts
; i
++)
907 const char *string
= APR_ARRAY_IDX(strings
, i
, const char *);
908 svn_stringbuf_appendbytes(new_str
, string
, strlen(string
));
909 svn_stringbuf_appendbytes(new_str
, separator
, sep_len
);
911 return new_str
->data
;
915 svn_cstring_casecmp(const char *str1
, const char *str2
)
919 const int a
= *str1
++;
920 const int b
= *str2
++;
921 const int cmp
= svn_ctype_casecmp(a
, b
);
928 svn_cstring_strtoui64(apr_uint64_t
*n
, const char *str
,
929 apr_uint64_t minval
, apr_uint64_t maxval
,
935 /* We assume errno is thread-safe. */
936 errno
= 0; /* APR-0.9 doesn't always set errno */
938 /* ### We're throwing away half the number range here.
939 * ### APR needs a apr_strtoui64() function. */
940 val
= apr_strtoi64(str
, &endptr
, base
);
941 if (errno
== EINVAL
|| endptr
== str
|| str
[0] == '\0' || *endptr
!= '\0')
942 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
943 _("Could not convert '%s' into a number"),
945 if ((errno
== ERANGE
&& (val
== APR_INT64_MIN
|| val
== APR_INT64_MAX
)) ||
946 val
< 0 || (apr_uint64_t
)val
< minval
|| (apr_uint64_t
)val
> maxval
)
947 /* ### Mark this for translation when gettext doesn't choke on macros. */
948 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
949 "Number '%s' is out of range "
950 "'[%" APR_UINT64_T_FMT
", %" APR_UINT64_T_FMT
"]'",
951 str
, minval
, maxval
);
957 svn_cstring_atoui64(apr_uint64_t
*n
, const char *str
)
959 return svn_error_trace(svn_cstring_strtoui64(n
, str
, 0,
960 APR_UINT64_MAX
, 10));
964 svn_cstring_atoui(unsigned int *n
, const char *str
)
968 SVN_ERR(svn_cstring_strtoui64(&val
, str
, 0, APR_UINT32_MAX
, 10));
969 *n
= (unsigned int)val
;
974 svn_cstring_strtoi64(apr_int64_t
*n
, const char *str
,
975 apr_int64_t minval
, apr_int64_t maxval
,
981 /* We assume errno is thread-safe. */
982 errno
= 0; /* APR-0.9 doesn't always set errno */
984 val
= apr_strtoi64(str
, &endptr
, base
);
985 if (errno
== EINVAL
|| endptr
== str
|| str
[0] == '\0' || *endptr
!= '\0')
986 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
987 _("Could not convert '%s' into a number"),
989 if ((errno
== ERANGE
&& (val
== APR_INT64_MIN
|| val
== APR_INT64_MAX
)) ||
990 val
< minval
|| val
> maxval
)
991 /* ### Mark this for translation when gettext doesn't choke on macros. */
992 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS
, NULL
,
993 "Number '%s' is out of range "
994 "'[%" APR_INT64_T_FMT
", %" APR_INT64_T_FMT
"]'",
995 str
, minval
, maxval
);
1001 svn_cstring_atoi64(apr_int64_t
*n
, const char *str
)
1003 return svn_error_trace(svn_cstring_strtoi64(n
, str
, APR_INT64_MIN
,
1004 APR_INT64_MAX
, 10));
1008 svn_cstring_atoi(int *n
, const char *str
)
1012 SVN_ERR(svn_cstring_strtoi64(&val
, str
, APR_INT32_MIN
, APR_INT32_MAX
, 10));
1014 return SVN_NO_ERROR
;
1019 svn__strtoff(apr_off_t
*offset
, const char *buf
, char **end
, int base
)
1021 #if !APR_VERSION_AT_LEAST(1,0,0)
1023 *offset
= strtol(buf
, end
, base
);
1024 return APR_FROM_OS_ERROR(errno
);
1026 return apr_strtoff(offset
, buf
, end
, base
);
1030 /* "Precalculated" itoa values for 2 places (including leading zeros).
1031 * For maximum performance, make sure all table entries are word-aligned.
1033 static const char decimal_table
[100][4]
1034 = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09"
1035 , "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"
1036 , "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"
1037 , "30", "31", "32", "33", "34", "35", "36", "37", "38", "39"
1038 , "40", "41", "42", "43", "44", "45", "46", "47", "48", "49"
1039 , "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"
1040 , "60", "61", "62", "63", "64", "65", "66", "67", "68", "69"
1041 , "70", "71", "72", "73", "74", "75", "76", "77", "78", "79"
1042 , "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"
1043 , "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"};
1045 /* Copy the two bytes at SOURCE[0] and SOURCE[1] to DEST[0] and DEST[1] */
1046 #define COPY_TWO_BYTES(dest,source)\
1047 memcpy((dest), (source), 2)
1050 svn__ui64toa(char * dest
, apr_uint64_t number
)
1052 char buffer
[SVN_INT64_BUFFER_SIZE
];
1053 apr_uint32_t reduced
; /* used for 32 bit DIV */
1056 /* Small numbers are by far the most common case.
1057 * Therefore, we use special code.
1063 dest
[0] = (char)('0' + number
);
1069 COPY_TWO_BYTES(dest
, decimal_table
[(apr_size_t
)number
]);
1075 /* Standard code. Write string in pairs of chars back-to-front */
1076 buffer
[SVN_INT64_BUFFER_SIZE
- 1] = 0;
1077 target
= &buffer
[SVN_INT64_BUFFER_SIZE
- 3];
1079 /* Loop may be executed 0 .. 2 times. */
1080 while (number
>= 100000000)
1082 /* Number is larger than 100^4, i.e. we can write 4x2 chars.
1083 * Also, use 32 bit DIVs as these are about twice as fast.
1085 reduced
= (apr_uint32_t
)(number
% 100000000);
1086 number
/= 100000000;
1088 COPY_TWO_BYTES(target
- 0, decimal_table
[reduced
% 100]);
1090 COPY_TWO_BYTES(target
- 2, decimal_table
[reduced
% 100]);
1092 COPY_TWO_BYTES(target
- 4, decimal_table
[reduced
% 100]);
1094 COPY_TWO_BYTES(target
- 6, decimal_table
[reduced
% 100]);
1098 /* Now, the number fits into 32 bits, but may still be larger than 99 */
1099 reduced
= (apr_uint32_t
)(number
);
1100 while (reduced
>= 100)
1102 COPY_TWO_BYTES(target
, decimal_table
[reduced
% 100]);
1107 /* The number is now smaller than 100 but larger than 1 */
1108 COPY_TWO_BYTES(target
, decimal_table
[reduced
]);
1110 /* Correction for uneven count of places. */
1114 /* Copy to target */
1115 memcpy(dest
, target
, &buffer
[SVN_INT64_BUFFER_SIZE
] - target
);
1116 return &buffer
[SVN_INT64_BUFFER_SIZE
] - target
- 1;
1120 svn__i64toa(char * dest
, apr_int64_t number
)
1123 return svn__ui64toa(dest
, (apr_uint64_t
)number
);
1126 return svn__ui64toa(dest
+ 1, (apr_uint64_t
)(0-number
)) + 1;
1130 ui64toa_sep(apr_uint64_t number
, char seperator
, char *buffer
)
1132 apr_size_t length
= svn__ui64toa(buffer
, number
);
1135 for (i
= length
; i
> 3; i
-= 3)
1137 memmove(&buffer
[i
- 2], &buffer
[i
- 3], length
- i
+ 3);
1138 buffer
[i
-3] = seperator
;
1146 svn__ui64toa_sep(apr_uint64_t number
, char seperator
, apr_pool_t
*pool
)
1148 char buffer
[2 * SVN_INT64_BUFFER_SIZE
];
1149 ui64toa_sep(number
, seperator
, buffer
);
1151 return apr_pstrdup(pool
, buffer
);
1155 svn__i64toa_sep(apr_int64_t number
, char seperator
, apr_pool_t
*pool
)
1157 char buffer
[2 * SVN_INT64_BUFFER_SIZE
];
1161 ui64toa_sep((apr_uint64_t
)(-number
), seperator
, &buffer
[1]);
1164 ui64toa_sep((apr_uint64_t
)(number
), seperator
, buffer
);
1166 return apr_pstrdup(pool
, buffer
);
1170 svn_cstring__similarity(const char *stra
, const char *strb
,
1171 svn_membuf_t
*buffer
, apr_size_t
*rlcs
)
1173 svn_string_t stringa
, stringb
;
1174 stringa
.data
= stra
;
1175 stringa
.len
= strlen(stra
);
1176 stringb
.data
= strb
;
1177 stringb
.len
= strlen(strb
);
1178 return svn_string__similarity(&stringa
, &stringb
, buffer
, rlcs
);
1182 svn_string__similarity(const svn_string_t
*stringa
,
1183 const svn_string_t
*stringb
,
1184 svn_membuf_t
*buffer
, apr_size_t
*rlcs
)
1186 const char *stra
= stringa
->data
;
1187 const char *strb
= stringb
->data
;
1188 const apr_size_t lena
= stringa
->len
;
1189 const apr_size_t lenb
= stringb
->len
;
1190 const apr_size_t total
= lena
+ lenb
;
1191 const char *enda
= stra
+ lena
;
1192 const char *endb
= strb
+ lenb
;
1195 /* Skip the common prefix ... */
1196 while (stra
< enda
&& strb
< endb
&& *stra
== *strb
)
1202 /* ... and the common suffix */
1203 while (stra
< enda
&& strb
< endb
)
1215 if (stra
< enda
&& strb
< endb
)
1217 const apr_size_t resta
= enda
- stra
;
1218 const apr_size_t restb
= endb
- strb
;
1219 const apr_size_t slots
= (resta
> restb
? restb
: resta
);
1220 apr_size_t
*curr
, *prev
;
1223 /* The outer loop must iterate on the longer string. */
1235 /* Allocate two columns in the LCS matrix
1236 ### Optimize this to (slots + 2) instesd of 2 * (slots + 1) */
1237 svn_membuf__ensure(buffer
, 2 * (slots
+ 1) * sizeof(apr_size_t
));
1238 svn_membuf__nzero(buffer
, (slots
+ 2) * sizeof(apr_size_t
));
1239 prev
= buffer
->data
;
1240 curr
= prev
+ slots
+ 1;
1242 /* Calculate LCS length of the remainder */
1243 for (pstr
= stra
; pstr
< enda
; ++pstr
)
1246 for (i
= 1; i
<= slots
; ++i
)
1248 if (*pstr
== strb
[i
-1])
1249 curr
[i
] = prev
[i
-1] + 1;
1251 curr
[i
] = (curr
[i
-1] > prev
[i
] ? curr
[i
-1] : prev
[i
]);
1254 /* Swap the buffers, making the previous one current */
1256 apr_size_t
*const temp
= prev
;
1268 /* Return similarity ratio rounded to 4 significant digits */
1270 return(unsigned int)((2000 * lcs
+ total
/2) / total
);