File: strview.h

package info (click to toggle)
graphviz 14.1.2-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 139,476 kB
  • sloc: ansic: 142,288; cpp: 11,975; python: 7,883; makefile: 4,044; yacc: 3,030; xml: 2,972; tcl: 2,495; sh: 1,391; objc: 1,159; java: 560; lex: 423; perl: 243; awk: 156; pascal: 139; php: 58; ruby: 49; cs: 31; sed: 1
file content (142 lines) | stat: -rw-r--r-- 3,569 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/// \file
/// \ingroup cgraph_utils
/// \brief Non-owning string references
///
/// This is similar to C++17’s `std::string_view`. Instances of `strview_t`
/// should generally be passed around by value rather than pointer as they are
/// small.

#pragma once

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <util/alloc.h>
#include <util/startswith.h>
#include <util/strcasecmp.h>

/// a non-owning string reference
typedef struct {
  const char *data; ///< start of the pointed to string
  size_t size;      ///< extent of the string in bytes
} strview_t;

/// create a string reference
static inline strview_t strview(const char *referent, char terminator) {

  assert(referent != NULL);

  // can we find the terminator before the end of the containing string?
  const char *end = strchr(referent, terminator);
  if (end != NULL) {
    return (strview_t){.data = referent, .size = (size_t)(end - referent)};
  }

  // otherwise, span the entire string
  return (strview_t){.data = referent, .size = strlen(referent)};
}

/// make a heap-allocated string from this string view
static inline char *strview_str(strview_t source) {

  assert(source.data != NULL);

  return gv_strndup(source.data, source.size);
}

/// compare two string references for case insensitive equality
static inline bool strview_case_eq(strview_t a, strview_t b) {

  assert(a.data != NULL);
  assert(b.data != NULL);

  if (a.size != b.size) {
    return false;
  }

  return strncasecmp(a.data, b.data, a.size) == 0;
}

/// compare a string reference to a string for case insensitive equality
static inline bool strview_case_str_eq(strview_t a, const char *b) {

  assert(a.data != NULL);
  assert(b != NULL);

  return strview_case_eq(a, strview(b, '\0'));
}

/// compare two string references
static inline int strview_cmp(strview_t a, strview_t b) {

  size_t min_size = a.size > b.size ? b.size : a.size;
  int cmp = strncmp(a.data, b.data, min_size);
  if (cmp != 0) {
    return cmp;
  }

  if (a.size > b.size) {
    return 1;
  }
  if (a.size < b.size) {
    return -1;
  }
  return 0;
}

/// compare two string references for equality
static inline bool strview_eq(strview_t a, strview_t b) {

  assert(a.data != NULL);
  assert(b.data != NULL);

  return strview_cmp(a, b) == 0;
}

/// compare a string reference to a string for equality
static inline bool strview_str_eq(strview_t a, const char *b) {

  assert(a.data != NULL);
  assert(b != NULL);

  return strview_eq(a, strview(b, '\0'));
}

/// does the given string appear as a substring of the string view?
static inline bool strview_str_contains(strview_t haystack,
                                        const char *needle) {

  assert(haystack.data != NULL);
  assert(needle != NULL);

  // the empty string is a substring of everything
  if (strcmp(needle, "") == 0) {
    return true;
  }

  for (size_t offset = 0; offset < haystack.size;) {

    // find the next possible starting point for the substring
    const char *candidate = (const char *)memchr(
        haystack.data + offset, needle[0], haystack.size - offset);
    if (candidate == NULL) {
      break;
    }

    // is it too close to the end of the containing string?
    if ((size_t)(haystack.data + haystack.size - candidate) < strlen(needle)) {
      return false;
    }

    // is it a match?
    if (startswith(candidate, needle)) {
      return true;
    }

    // advance to the position after this match for the next scan
    offset = (size_t)(candidate - haystack.data) + 1;
  }

  return false;
}