Commit 0efd4a9a authored by Kirill Smelkov's avatar Kirill Smelkov

strings: New package

Provide string utilities to verify whether string has suffix/prefix,
trim it, and split string by a delimiter. The code originated in wcfs
codebase in wendelin.core .

Pyx/nogil only.
parent 309963f8
...@@ -11,6 +11,8 @@ include golang/errors.h ...@@ -11,6 +11,8 @@ include golang/errors.h
include golang/errors.cpp include golang/errors.cpp
include golang/fmt.h include golang/fmt.h
include golang/fmt.cpp include golang/fmt.cpp
include golang/strings.h
include golang/strings.cpp
include golang/sync.h include golang/sync.h
include golang/sync.cpp include golang/sync.cpp
include golang/time.h include golang/time.h
......
...@@ -229,6 +229,11 @@ cdef extern from * nogil: ...@@ -229,6 +229,11 @@ cdef extern from * nogil:
extern void _test_global(); extern void _test_global();
extern void _test_fmt_sprintf_cpp(); extern void _test_fmt_sprintf_cpp();
extern void _test_fmt_errorf_cpp(); extern void _test_fmt_errorf_cpp();
extern void _test_strings_has_prefix();
extern void _test_strings_trim_prefix();
extern void _test_strings_has_suffix();
extern void _test_strings_trim_suffix();
extern void _test_strings_split();
extern void _test_sync_once_cpp(); extern void _test_sync_once_cpp();
""" """
void _test_chan_cpp_refcount() except +topyexc void _test_chan_cpp_refcount() except +topyexc
...@@ -244,6 +249,11 @@ cdef extern from * nogil: ...@@ -244,6 +249,11 @@ cdef extern from * nogil:
void _test_global() except +topyexc void _test_global() except +topyexc
void _test_fmt_sprintf_cpp() except +topyexc void _test_fmt_sprintf_cpp() except +topyexc
void _test_fmt_errorf_cpp() except +topyexc void _test_fmt_errorf_cpp() except +topyexc
void _test_strings_has_prefix() except +topyexc
void _test_strings_trim_prefix() except +topyexc
void _test_strings_has_suffix() except +topyexc
void _test_strings_trim_suffix() except +topyexc
void _test_strings_split() except +topyexc
void _test_sync_once_cpp() except +topyexc void _test_sync_once_cpp() except +topyexc
def test_chan_cpp_refcount(): def test_chan_cpp_refcount():
with nogil: with nogil:
...@@ -284,6 +294,21 @@ def test_fmt_sprintf_cpp(): # TODO move -> _fmt_test.pyx ...@@ -284,6 +294,21 @@ def test_fmt_sprintf_cpp(): # TODO move -> _fmt_test.pyx
def test_fmt_errorf_cpp(): def test_fmt_errorf_cpp():
with nogil: with nogil:
_test_fmt_errorf_cpp() _test_fmt_errorf_cpp()
def test_strings_has_prefix(): # TODO move -> _sync_test.pyx
with nogil:
_test_strings_has_prefix()
def test_strings_trim_prefix():
with nogil:
_test_strings_trim_prefix()
def test_strings_has_suffix():
with nogil:
_test_strings_has_suffix()
def test_strings_trim_suffix():
with nogil:
_test_strings_trim_suffix()
def test_strings_split():
with nogil:
_test_strings_split()
def test_sync_once_cpp(): # TODO move -> _sync_test.pyx def test_sync_once_cpp(): # TODO move -> _sync_test.pyx
with nogil: with nogil:
_test_sync_once_cpp() _test_sync_once_cpp()
......
...@@ -176,6 +176,8 @@ def Extension(name, sources, **kw): ...@@ -176,6 +176,8 @@ def Extension(name, sources, **kw):
dependv.append('%s/golang/_errors.pxd' % pygo) dependv.append('%s/golang/_errors.pxd' % pygo)
dependv.append('%s/golang/fmt.h' % pygo) dependv.append('%s/golang/fmt.h' % pygo)
dependv.append('%s/golang/fmt.pxd' % pygo) dependv.append('%s/golang/fmt.pxd' % pygo)
dependv.append('%s/golang/strings.h' % pygo)
dependv.append('%s/golang/strings.pxd' % pygo)
dependv.append('%s/golang/sync.h' % pygo) dependv.append('%s/golang/sync.h' % pygo)
dependv.append('%s/golang/sync.pxd' % pygo) dependv.append('%s/golang/sync.pxd' % pygo)
dependv.append('%s/golang/_sync.pxd' % pygo) dependv.append('%s/golang/_sync.pxd' % pygo)
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "golang/libgolang.h" #include "golang/libgolang.h"
#include "golang/fmt.h" #include "golang/fmt.h"
#include "golang/strings.h"
#include "golang/sync.h" #include "golang/sync.h"
#include "golang/time.h" #include "golang/time.h"
...@@ -749,6 +750,100 @@ void _test_fmt_errorf_cpp() { ...@@ -749,6 +750,100 @@ void _test_fmt_errorf_cpp() {
ASSERT_EQ(fmt::errorf(f, "read", myfile, myerror)->Error() , "read myfile: myerror"); ASSERT_EQ(fmt::errorf(f, "read", myfile, myerror)->Error() , "read myfile: myerror");
} }
// ---- strings:: ----
void _test_strings_has_prefix() {
ASSERT(strings::has_prefix("", "") == true);
ASSERT(strings::has_prefix("", "a") == false);
ASSERT(strings::has_prefix("", 'a') == false);
ASSERT(strings::has_prefix("b", "a") == false);
ASSERT(strings::has_prefix("b", 'a') == false);
ASSERT(strings::has_prefix("a", "a") == true);
ASSERT(strings::has_prefix("a", 'a') == true);
ASSERT(strings::has_prefix("a", "aa") == false);
ASSERT(strings::has_prefix("hello", "") == true);
ASSERT(strings::has_prefix("hello", "h") == true);
ASSERT(strings::has_prefix("hello", 'h') == true);
ASSERT(strings::has_prefix("hello", 'X') == false);
ASSERT(strings::has_prefix("hello", "he") == true);
ASSERT(strings::has_prefix("hello", "hel") == true);
ASSERT(strings::has_prefix("hello", "hez") == false);
ASSERT(strings::has_prefix("hello", "a") == false);
}
void _test_strings_trim_prefix() {
ASSERT_EQ(strings::trim_prefix("", "") , "");
ASSERT_EQ(strings::trim_prefix("", "a") , "");
ASSERT_EQ(strings::trim_prefix("", 'a') , "");
ASSERT_EQ(strings::trim_prefix("a", "") , "a");
ASSERT_EQ(strings::trim_prefix("a", "b") , "a");
ASSERT_EQ(strings::trim_prefix("a", 'b') , "a");
ASSERT_EQ(strings::trim_prefix("a", "a") , "");
ASSERT_EQ(strings::trim_prefix("a", 'a') , "");
ASSERT_EQ(strings::trim_prefix("a", "ab") , "a");
ASSERT_EQ(strings::trim_prefix("hello", "world") , "hello");
ASSERT_EQ(strings::trim_prefix("hello", "h") , "ello");
ASSERT_EQ(strings::trim_prefix("hello", 'h') , "ello");
ASSERT_EQ(strings::trim_prefix("hello", "he") , "llo");
ASSERT_EQ(strings::trim_prefix("hello", "hel") , "lo");
ASSERT_EQ(strings::trim_prefix("hello", "hez") , "hello");
}
void _test_strings_has_suffix() {
ASSERT(strings::has_suffix("", "") == true);
ASSERT(strings::has_suffix("", "a") == false);
ASSERT(strings::has_suffix("", 'a') == false);
ASSERT(strings::has_suffix("b", "a") == false);
ASSERT(strings::has_suffix("b", 'a') == false);
ASSERT(strings::has_suffix("a", "a") == true);
ASSERT(strings::has_suffix("a", 'a') == true);
ASSERT(strings::has_suffix("a", "aa") == false);
ASSERT(strings::has_suffix("hello", "") == true);
ASSERT(strings::has_suffix("hello", "o") == true);
ASSERT(strings::has_suffix("hello", 'o') == true);
ASSERT(strings::has_suffix("hello", 'X') == false);
ASSERT(strings::has_suffix("hello", "lo") == true);
ASSERT(strings::has_suffix("hello", "llo") == true);
ASSERT(strings::has_suffix("hello", "llz") == false);
ASSERT(strings::has_suffix("hello", "a") == false);
}
void _test_strings_trim_suffix() {
ASSERT_EQ(strings::trim_suffix("", "") , "");
ASSERT_EQ(strings::trim_suffix("", "a") , "");
ASSERT_EQ(strings::trim_suffix("", 'a') , "");
ASSERT_EQ(strings::trim_suffix("a", "") , "a");
ASSERT_EQ(strings::trim_suffix("a", "b") , "a");
ASSERT_EQ(strings::trim_suffix("a", 'b') , "a");
ASSERT_EQ(strings::trim_suffix("a", "a") , "");
ASSERT_EQ(strings::trim_suffix("a", 'a') , "");
ASSERT_EQ(strings::trim_suffix("a", "ab") , "a");
ASSERT_EQ(strings::trim_suffix("hello", "world") , "hello");
ASSERT_EQ(strings::trim_suffix("hello", "o") , "hell");
ASSERT_EQ(strings::trim_suffix("hello", 'o') , "hell");
ASSERT_EQ(strings::trim_suffix("hello", "lo") , "hel");
ASSERT_EQ(strings::trim_suffix("hello", "llo") , "he");
ASSERT_EQ(strings::trim_suffix("hello", "llz") , "hello");
}
void _test_strings_split() {
auto V = [](const std::initializer_list<string> &argv) -> vector<string> {
return argv;
};
ASSERT_EQ(strings::split("" , ' ') , V({}));
ASSERT_EQ(strings::split("a" , ' ') , V({"a"}));
ASSERT_EQ(strings::split("a " , ' ') , V({"a", ""}));
ASSERT_EQ(strings::split(" a" , ' ') , V({"", "a"}));
ASSERT_EQ(strings::split("ab " , ' ') , V({"ab", ""}));
ASSERT_EQ(strings::split("ab c" , ' ') , V({"ab", "c"}));
ASSERT_EQ(strings::split("ab cd" , ' ') , V({"ab", "cd"}));
ASSERT_EQ(strings::split("ab cd " , ' ') , V({"ab", "cd", ""}));
ASSERT_EQ(strings::split("ab cd e" , ' ') , V({"ab", "cd", "e"}));
ASSERT_EQ(strings::split(" ab cd e" , ' ') , V({"", "ab", "cd", "e"}));
ASSERT_EQ(strings::split(" ab cd e" , ' ') , V({"", "", "ab", "cd", "e"}));
}
// ---- sync:: ---- // ---- sync:: ----
// verify that sync::Once works. // verify that sync::Once works.
......
// Copyright (C) 2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package strings mirrors Go package strings.
// See strings.h for package overview.
#include "golang/strings.h"
using std::vector;
// golang::strings::
namespace golang {
namespace strings {
bool has_prefix(const string &s, const string &prefix) {
return s.compare(0, prefix.size(), prefix) == 0;
}
bool has_prefix(const string &s, char prefix) {
return (s.size() >= 1 && s[0] == prefix);
}
bool has_suffix(const string &s, const string &suffix) {
return (s.size() >= suffix.size() &&
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0);
}
bool has_suffix(const string &s, char suffix) {
return (s.size() >= 1 && s[s.size()-1] == suffix);
}
string trim_prefix(const string &s, const string &prefix) {
if (!has_prefix(s, prefix))
return s;
return s.substr(prefix.size());
}
string trim_prefix(const string &s, char prefix) {
if (!has_prefix(s, prefix))
return s;
return s.substr(1);
}
string trim_suffix(const string &s, const string &suffix) {
if (!has_suffix(s, suffix))
return s;
return s.substr(0, s.size()-suffix.size());
}
string trim_suffix(const string &s, char suffix) {
if (!has_suffix(s, suffix))
return s;
return s.substr(0, s.size()-1);
}
vector<string> split(const string &s, char sep) {
vector<string> r;
int psep_prev=-1;
size_t psep;
if (s.size() == 0)
return r;
while (1) {
psep = s.find(sep, psep_prev+1);
if (psep == string::npos) {
r.push_back(s.substr(psep_prev+1));
return r;
}
r.push_back(s.substr(psep_prev+1, psep-(psep_prev+1)));
psep_prev = psep;
}
}
}} // golang::strings::
#ifndef _NXD_LIBGOLANG_STRINGS_H
#define _NXD_LIBGOLANG_STRINGS_H
// Copyright (C) 2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package strings mirrors Go package strings.
//
// - `has_prefix` checks whether string starts from prefix.
// - `has_suffix` checks whether string ends with suffix.
// - `trim_prefix` removes prefix from a string.
// - `trim_suffix` removes suffix from a string.
// - `split` splits string by delimiter.
//
// See also https://golang.org/pkg/strings for Go strings package documentation.
#include <golang/libgolang.h>
#include <vector>
// golang::strings::
namespace golang {
namespace strings {
// has_prefix checks whether string starts from prefix.
LIBGOLANG_API bool has_prefix(const string &s, const string &prefix);
LIBGOLANG_API bool has_prefix(const string &s, char prefix);
// has_suffix checks whether string ends with suffix.
LIBGOLANG_API bool has_suffix(const string &s, const string &suffix);
LIBGOLANG_API bool has_suffix(const string &s, char suffix);
// trim_prefix removes prefix from string s.
//
// If s does not start from prefix, nothing is removed.
LIBGOLANG_API string trim_prefix(const string &s, const string &prefix);
LIBGOLANG_API string trim_prefix(const string &s, char prefix);
// trim_suffix removes suffix from string s.
//
// If s does not end with suffix, nothing is removed.
LIBGOLANG_API string trim_suffix(const string &s, const string &suffix);
LIBGOLANG_API string trim_suffix(const string &s, char suffix);
// split splits string s by separator sep.
//
// For example split("hello world zzz", ' ') -> ["hello", "world", "zzz"].
LIBGOLANG_API std::vector<string> split(const string &s, char sep);
}} // golang::strings::
#endif // _NXD_LIBGOLANG_STRINGS_H
# cython: language_level=2
# Copyright (C) 2019 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""Package strings mirrors Go package strings.
- `has_prefix` checks whether string starts from prefix.
- `has_suffix` checks whether string ends with suffix.
- `trim_prefix` removes prefix from a string.
- `trim_suffix` removes suffix from a string.
- `split` splits string by delimiter.
See also https://golang.org/pkg/strings for Go strings package documentation.
"""
from golang cimport string
from libcpp.vector cimport vector
cdef extern from *:
ctypedef bint cbool "bool"
cdef extern from "golang/strings.h" namespace "golang::strings" nogil:
cbool has_prefix(const string &s, const string &prefix);
cbool has_prefix(const string &s, char prefix);
cbool has_suffix(const string &s, const string &suffix);
cbool has_suffix(const string &s, char suffix);
string trim_prefix(const string &s, const string &prefix);
string trim_prefix(const string &s, char prefix);
string trim_suffix(const string &s, const string &suffix);
string trim_suffix(const string &s, char suffix);
vector[string] split(const string &s, char sep);
...@@ -197,6 +197,7 @@ setup( ...@@ -197,6 +197,7 @@ setup(
'golang/context.cpp', 'golang/context.cpp',
'golang/errors.cpp', 'golang/errors.cpp',
'golang/fmt.cpp', 'golang/fmt.cpp',
'golang/strings.cpp',
'golang/sync.cpp', 'golang/sync.cpp',
'golang/time.cpp'], 'golang/time.cpp'],
depends = [ depends = [
...@@ -205,6 +206,7 @@ setup( ...@@ -205,6 +206,7 @@ setup(
'golang/cxx.h', 'golang/cxx.h',
'golang/errors.h', 'golang/errors.h',
'golang/fmt.h', 'golang/fmt.h',
'golang/strings.h',
'golang/sync.h', 'golang/sync.h',
'golang/time.h'], 'golang/time.h'],
include_dirs = ['.', '3rdparty/include'], include_dirs = ['.', '3rdparty/include'],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment