Skip to content

Commit 0994abe

Browse files
committed
Add utility function x_strlcpy() and a test
1 parent 47272df commit 0994abe

9 files changed

+276
-4
lines changed

.gitignore

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
/_old*
44
/_ref
55
/autom4te.cache
6-
/Debug
7-
/ipch
8-
/Release
9-
/x64
6+
Debug
7+
ipch
8+
Release
9+
x64
1010
*.la
1111
*.lo
1212
*.opensdf

lib_mysqludf_str.sln

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 11.00
33
# Visual C++ Express 2010
44
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_mysqludf_str", "lib_mysqludf_str.vcxproj", "{F388FC46-2D69-4B68-8824-65D266D6DF82}"
55
EndProject
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "string_utils_test", "tests\string_utils_test\string_utils_test.vcxproj", "{82B320D7-A81D-4CF5-BF11-A8800A753D4F}"
7+
EndProject
68
Global
79
GlobalSection(SolutionConfigurationPlatforms) = preSolution
810
Debug|Win32 = Debug|Win32
@@ -19,6 +21,11 @@ Global
1921
{F388FC46-2D69-4B68-8824-65D266D6DF82}.Release|Win32.Build.0 = Release|Win32
2022
{F388FC46-2D69-4B68-8824-65D266D6DF82}.Release|x64.ActiveCfg = Release|x64
2123
{F388FC46-2D69-4B68-8824-65D266D6DF82}.Release|x64.Build.0 = Release|x64
24+
{82B320D7-A81D-4CF5-BF11-A8800A753D4F}.Debug|Win32.ActiveCfg = Debug|x64
25+
{82B320D7-A81D-4CF5-BF11-A8800A753D4F}.Debug|x64.ActiveCfg = Debug|x64
26+
{82B320D7-A81D-4CF5-BF11-A8800A753D4F}.Debug|x64.Build.0 = Debug|x64
27+
{82B320D7-A81D-4CF5-BF11-A8800A753D4F}.Release|Win32.ActiveCfg = Release|x64
28+
{82B320D7-A81D-4CF5-BF11-A8800A753D4F}.Release|x64.ActiveCfg = Release|x64
2229
EndGlobalSection
2330
GlobalSection(SolutionProperties) = preSolution
2431
HideSolutionNode = FALSE

lib_mysqludf_str.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,11 @@
139139
<ItemGroup>
140140
<ClCompile Include="char_vector.c" />
141141
<ClCompile Include="lib_mysqludf_str.c" />
142+
<ClCompile Include="x_strlcpy.c" />
142143
</ItemGroup>
143144
<ItemGroup>
144145
<ClInclude Include="char_vector.h" />
146+
<ClInclude Include="string_utils.h" />
145147
</ItemGroup>
146148
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
147149
<ImportGroup Label="ExtensionTargets">

lib_mysqludf_str.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@
2121
<ClCompile Include="char_vector.c">
2222
<Filter>Source Files</Filter>
2323
</ClCompile>
24+
<ClCompile Include="x_strlcpy.c">
25+
<Filter>Source Files</Filter>
26+
</ClCompile>
2427
</ItemGroup>
2528
<ItemGroup>
2629
<ClInclude Include="char_vector.h">
2730
<Filter>Header Files</Filter>
2831
</ClInclude>
32+
<ClInclude Include="string_utils.h">
33+
<Filter>Header Files</Filter>
34+
</ClInclude>
2935
</ItemGroup>
3036
</Project>

string_utils.h

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* -*- coding: utf-8; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: t -*- */
2+
3+
// This code and all comments, written by Daniel Trebbien, are hereby entered into the Public Domain by their author.
4+
5+
#pragma once
6+
#ifndef LIB_MYSQLUDF_STR_STRING_UTILS_H
7+
#define LIB_MYSQLUDF_STR_STRING_UTILS_H 1
8+
#include <stddef.h>
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* Copies the string pointed to by \p src to the output buffer pointed to by \p dest, which has
16+
* space for \p dest_len bytes. If \p dest_len is 0, then the function simply returns
17+
* <code>strlen(src)</code>. Otherwise, at most <code>dest_len - 1</code> characters are
18+
* copied and \p dest is always NUL-terminated.
19+
*
20+
* \returns the length of the string pointed to by \p src. If the return value is greater than
21+
* or equal to \p dest_len, then truncation occurred.
22+
*/
23+
size_t x_strlcpy(char *__restrict dest, const char *__restrict src, size_t dest_len);
24+
25+
#ifdef __cplusplus
26+
}
27+
#endif
28+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* -*- coding: utf-8; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: t -*- */
2+
3+
// This code and all comments, written by Daniel Trebbien, are hereby entered into the Public Domain by their author.
4+
5+
#include <algorithm>
6+
7+
#define BOOST_TEST_DYN_LINK 1
8+
#define BOOST_TEST_MODULE "string_utils tests"
9+
#include <boost/test/unit_test.hpp>
10+
11+
#include "../../string_utils.h"
12+
13+
BOOST_AUTO_TEST_CASE(test_x_strlcpy)
14+
{
15+
char buf[10];
16+
17+
// No room for the NUL terminator
18+
buf[0] = 'a';
19+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 0), 4);
20+
BOOST_CHECK_EQUAL(buf[0], 'a');
21+
22+
// Only room for the NUL terminator and `*src` is a NUL character
23+
buf[0] = buf[1] = 'a';
24+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "\0", 1), 0);
25+
BOOST_CHECK_EQUAL(buf[0], '\0');
26+
BOOST_CHECK_EQUAL(buf[1], 'a');
27+
28+
// `*src` is a NUL character and `dest` has space for characters other than the NUL terminator.
29+
buf[0] = buf[1] = 'a';
30+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "\0", 10), 0);
31+
BOOST_CHECK_EQUAL(buf[0], '\0');
32+
BOOST_CHECK_EQUAL(buf[1], 'a');
33+
34+
// Only room for the NUL terminator and `*src` is not a NUL character
35+
buf[0] = buf[1] = 'a';
36+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 1), 4);
37+
BOOST_CHECK_EQUAL(buf[0], '\0');
38+
BOOST_CHECK_EQUAL(buf[1], 'a');
39+
40+
// Only room to copy one character from `src`
41+
std::fill(buf, buf + (sizeof buf), 'a');
42+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 2), 4);
43+
BOOST_CHECK_EQUAL(buf[0], 't');
44+
BOOST_CHECK_EQUAL(buf[1], '\0');
45+
BOOST_CHECK_EQUAL(buf[2], 'a');
46+
47+
// Only room to copy three characters from `src`
48+
std::fill(buf, buf + (sizeof buf), 'a');
49+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 4), 4);
50+
BOOST_CHECK_EQUAL(buf[0], 't');
51+
BOOST_CHECK_EQUAL(buf[1], 'e');
52+
BOOST_CHECK_EQUAL(buf[2], 's');
53+
BOOST_CHECK_EQUAL(buf[3], '\0');
54+
BOOST_CHECK_EQUAL(buf[4], 'a');
55+
56+
// `dest` is just big enough to copy `src` and the NUL terminator.
57+
std::fill(buf, buf + (sizeof buf), 'a');
58+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 5), 4);
59+
BOOST_CHECK_EQUAL(buf[0], 't');
60+
BOOST_CHECK_EQUAL(buf[1], 'e');
61+
BOOST_CHECK_EQUAL(buf[2], 's');
62+
BOOST_CHECK_EQUAL(buf[3], 't');
63+
BOOST_CHECK_EQUAL(buf[4], '\0');
64+
BOOST_CHECK_EQUAL(buf[5], 'a');
65+
66+
// `dest` has space for more than `strlen(src) + 1` characters.
67+
std::fill(buf, buf + (sizeof buf), 'a');
68+
BOOST_CHECK_EQUAL(x_strlcpy(buf, "test", 10), 4);
69+
BOOST_CHECK_EQUAL(buf[0], 't');
70+
BOOST_CHECK_EQUAL(buf[1], 'e');
71+
BOOST_CHECK_EQUAL(buf[2], 's');
72+
BOOST_CHECK_EQUAL(buf[3], 't');
73+
BOOST_CHECK_EQUAL(buf[4], '\0');
74+
BOOST_CHECK_EQUAL(buf[5], 'a');
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup Label="ProjectConfigurations">
4+
<ProjectConfiguration Include="Debug|x64">
5+
<Configuration>Debug</Configuration>
6+
<Platform>x64</Platform>
7+
</ProjectConfiguration>
8+
<ProjectConfiguration Include="Release|x64">
9+
<Configuration>Release</Configuration>
10+
<Platform>x64</Platform>
11+
</ProjectConfiguration>
12+
</ItemGroup>
13+
<PropertyGroup Label="Globals">
14+
<ProjectGuid>{82B320D7-A81D-4CF5-BF11-A8800A753D4F}</ProjectGuid>
15+
<Keyword>Win32Proj</Keyword>
16+
<RootNamespace>string_utils_test</RootNamespace>
17+
</PropertyGroup>
18+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
19+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
20+
<ConfigurationType>Application</ConfigurationType>
21+
<UseDebugLibraries>true</UseDebugLibraries>
22+
<CharacterSet>Unicode</CharacterSet>
23+
<PlatformToolset>Windows7.1SDK</PlatformToolset>
24+
</PropertyGroup>
25+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
26+
<ConfigurationType>Application</ConfigurationType>
27+
<UseDebugLibraries>false</UseDebugLibraries>
28+
<WholeProgramOptimization>true</WholeProgramOptimization>
29+
<CharacterSet>Unicode</CharacterSet>
30+
<PlatformToolset>Windows7.1SDK</PlatformToolset>
31+
</PropertyGroup>
32+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
33+
<ImportGroup Label="ExtensionSettings">
34+
</ImportGroup>
35+
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
36+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
37+
</ImportGroup>
38+
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
39+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
40+
</ImportGroup>
41+
<PropertyGroup Label="UserMacros" />
42+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
43+
<LinkIncremental>true</LinkIncremental>
44+
<IncludePath>C:\Program Files\Boost\include;$(IncludePath)</IncludePath>
45+
<LibraryPath>C:\Program Files\Boost\lib;$(LibraryPath)</LibraryPath>
46+
</PropertyGroup>
47+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
48+
<LinkIncremental>false</LinkIncremental>
49+
<IncludePath>C:\Program Files\Boost\include;$(IncludePath)</IncludePath>
50+
<LibraryPath>C:\Program Files\Boost\lib;$(LibraryPath)</LibraryPath>
51+
</PropertyGroup>
52+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
53+
<ClCompile>
54+
<PrecompiledHeader>
55+
</PrecompiledHeader>
56+
<WarningLevel>Level3</WarningLevel>
57+
<Optimization>Disabled</Optimization>
58+
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
</ClCompile>
60+
<Link>
61+
<SubSystem>Console</SubSystem>
62+
<GenerateDebugInformation>true</GenerateDebugInformation>
63+
<AdditionalDependencies>boost_unit_test_framework-vc100-mt-gd-1_47.lib;%(AdditionalDependencies)</AdditionalDependencies>
64+
</Link>
65+
</ItemDefinitionGroup>
66+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
67+
<ClCompile>
68+
<WarningLevel>Level3</WarningLevel>
69+
<PrecompiledHeader>
70+
</PrecompiledHeader>
71+
<Optimization>MaxSpeed</Optimization>
72+
<FunctionLevelLinking>true</FunctionLevelLinking>
73+
<IntrinsicFunctions>true</IntrinsicFunctions>
74+
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
75+
</ClCompile>
76+
<Link>
77+
<SubSystem>Console</SubSystem>
78+
<GenerateDebugInformation>true</GenerateDebugInformation>
79+
<EnableCOMDATFolding>true</EnableCOMDATFolding>
80+
<OptimizeReferences>true</OptimizeReferences>
81+
<AdditionalDependencies>boost_unit_test_framework-vc100-mt-1_47.lib;%(AdditionalDependencies)</AdditionalDependencies>
82+
</Link>
83+
</ItemDefinitionGroup>
84+
<ItemGroup>
85+
<ClCompile Include="..\..\x_strlcpy.c" />
86+
<ClCompile Include="string_utils_test.cpp" />
87+
</ItemGroup>
88+
<ItemGroup>
89+
<ClInclude Include="..\..\string_utils.h" />
90+
</ItemGroup>
91+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
92+
<ImportGroup Label="ExtensionTargets">
93+
</ImportGroup>
94+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup>
4+
<Filter Include="Source Files">
5+
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6+
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7+
</Filter>
8+
<Filter Include="Header Files">
9+
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10+
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11+
</Filter>
12+
<Filter Include="Resource Files">
13+
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14+
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15+
</Filter>
16+
</ItemGroup>
17+
<ItemGroup>
18+
<ClCompile Include="string_utils_test.cpp">
19+
<Filter>Source Files</Filter>
20+
</ClCompile>
21+
<ClCompile Include="..\..\x_strlcpy.c">
22+
<Filter>Source Files</Filter>
23+
</ClCompile>
24+
</ItemGroup>
25+
<ItemGroup>
26+
<ClInclude Include="..\..\string_utils.h">
27+
<Filter>Header Files</Filter>
28+
</ClInclude>
29+
</ItemGroup>
30+
</Project>

x_strlcpy.c

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* -*- coding: utf-8; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: t -*- */
2+
3+
// This code and all comments, written by Daniel Trebbien, are hereby entered into the Public Domain by their author.
4+
5+
#include <assert.h>
6+
#include <string.h>
7+
8+
#include "string_utils.h"
9+
10+
size_t x_strlcpy(char *__restrict dest, const char *__restrict src, size_t dest_len)
11+
{
12+
if (dest_len == 0) {
13+
return strlen(src);
14+
} else {
15+
char *const dest_str_end = dest + dest_len - 1;
16+
char *__restrict d = dest;
17+
18+
while (d != dest_str_end)
19+
{
20+
if ((*d++ = *src++) == '\0') {
21+
assert(*(src - 1) == '\0');
22+
assert(*(d - 1) == '\0');
23+
return (d - 1 - dest);
24+
}
25+
}
26+
27+
*d = '\0';
28+
return (dest_len - 1) + strlen(src);
29+
}
30+
}

0 commit comments

Comments
 (0)