Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
36c3f8e9
Commit
36c3f8e9
authored
May 31, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Stringification for all.
parent
47194adc
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
686 additions
and
582 deletions
+686
-582
blob.h
c++/src/capnproto/blob.h
+15
-0
capnpc-capnp.c++
c++/src/capnproto/compiler/capnpc-capnp.c++
+7
-10
dynamic.c++
c++/src/capnproto/dynamic.c++
+21
-0
dynamic.h
c++/src/capnproto/dynamic.h
+23
-1
generated-header-support.h
c++/src/capnproto/generated-header-support.h
+30
-4
layout.h
c++/src/capnproto/layout.h
+1
-0
schema-loader-test.c++
c++/src/capnproto/schema-loader-test.c++
+4
-6
schema.h
c++/src/capnproto/schema.h
+3
-1
stringify-test.c++
c++/src/capnproto/stringify-test.c++
+38
-10
stringify.c++
c++/src/capnproto/stringify.c++
+22
-6
stringify.h
c++/src/capnproto/stringify.h
+0
-37
test.capnp
c++/src/capnproto/test.capnp
+6
-0
common.h
c++/src/kj/common.h
+8
-0
exception.c++
c++/src/kj/exception.c++
+5
-5
exception.h
c++/src/kj/exception.h
+2
-3
logging.c++
c++/src/kj/logging.c++
+6
-6
logging.h
c++/src/kj/logging.h
+2
-2
string-test.c++
c++/src/kj/string-test.c++
+2
-2
string.c++
c++/src/kj/string.c++
+281
-0
string.h
c++/src/kj/string.h
+172
-0
util.c++
c++/src/kj/util.c++
+0
-303
util.h
c++/src/kj/util.h
+0
-182
Compiler.hs
compiler/src/Compiler.hs
+4
-0
CxxGenerator.hs
compiler/src/CxxGenerator.hs
+3
-0
Semantics.hs
compiler/src/Semantics.hs
+1
-0
c++-header.mustache
compiler/src/c++-header.mustache
+26
-4
c++-source.mustache
compiler/src/c++-source.mustache
+4
-0
No files found.
c++/src/capnproto/blob.h
View file @
36c3f8e9
...
...
@@ -25,6 +25,7 @@
#define CAPNPROTO_BLOB_H_
#include <kj/common.h>
#include <kj/string.h>
#include "common.h"
#include <string.h>
...
...
@@ -133,6 +134,11 @@ public:
inline
operator
const
char
*
()
const
{
return
data
();
}
};
inline
kj
::
StringPtr
KJ_STRINGIFY
(
Text
::
Reader
reader
)
{
// TODO(soon): Use size().
return
reader
.
c_str
();
}
class
Data
::
Builder
{
// Like Data::Reader except the pointers aren't const, and it can't be implicitly constructed from
// other types.
...
...
@@ -187,6 +193,8 @@ public:
inline
char
*
begin
()
const
{
return
bytes
;
}
inline
char
*
end
()
const
{
return
bytes
+
size_
;
}
inline
Data
::
Reader
asReader
()
{
return
Data
::
Reader
(
bytes
,
size_
);
}
private
:
char
*
bytes
;
uint
size_
;
...
...
@@ -207,10 +215,17 @@ public:
inline
operator
char
*
()
const
{
return
data
();
}
inline
operator
const
char
*
()
const
{
return
data
();
}
inline
Text
::
Reader
asReader
()
{
return
Text
::
Reader
(
begin
(),
size
());
}
private
:
static
char
nulstr
[
1
];
};
inline
kj
::
StringPtr
KJ_STRINGIFY
(
Text
::
Builder
builder
)
{
// TODO(soon): Use size().
return
builder
.
c_str
();
}
inline
bool
operator
==
(
const
char
*
a
,
Data
::
Reader
b
)
{
return
Data
::
Reader
(
a
)
==
b
;
}
inline
bool
operator
==
(
const
char
*
a
,
Data
::
Builder
b
)
{
return
Data
::
Reader
(
a
)
==
(
Data
::
Reader
)
b
;
}
inline
bool
operator
==
(
Data
::
Reader
a
,
Data
::
Builder
b
)
{
return
a
==
(
Data
::
Reader
)
b
;
}
...
...
c++/src/capnproto/compiler/capnpc-capnp.c++
View file @
36c3f8e9
...
...
@@ -32,7 +32,6 @@
#include <kj/io.h>
#include "../schema-loader.h"
#include "../dynamic.h"
#include "../stringify.h"
#include <unistd.h>
#include <unordered_map>
#include <vector>
...
...
@@ -67,8 +66,8 @@ private:
void
fill
(
char
*
textPos
,
Branch
*
branchesPos
,
TextBlob
&&
first
,
Rest
&&
...
rest
);
template
<
typename
T
>
auto
toContainer
(
T
&&
t
)
->
decltype
(
kj
::
STR
*
t
)
{
return
kj
::
STR
*
t
;
auto
toContainer
(
T
&&
t
)
->
decltype
(
kj
::
toCharSequence
(
kj
::
fwd
(
t
))
)
{
return
kj
::
toCharSequence
(
kj
::
fwd
(
t
))
;
}
TextBlob
&&
toContainer
(
TextBlob
&&
t
)
{
return
kj
::
mv
(
t
);
...
...
@@ -135,7 +134,7 @@ void TextBlob::fill(char* textPos, Branch* branchesPos) {
template
<
typename
First
,
typename
...
Rest
>
void
TextBlob
::
fill
(
char
*
textPos
,
Branch
*
branchesPos
,
First
&&
first
,
Rest
&&
...
rest
)
{
textPos
=
kj
::
fill
(
textPos
,
kj
::
fwd
<
First
>
(
first
));
textPos
=
kj
::
internal
::
fill
(
textPos
,
kj
::
fwd
<
First
>
(
first
));
fill
(
textPos
,
branchesPos
,
kj
::
fwd
<
Rest
>
(
rest
)...);
}
...
...
@@ -206,8 +205,6 @@ struct Indent {
inline
Iterator
end
()
const
{
return
Iterator
(
amount
);
}
};
inline
Indent
operator
*
(
kj
::
Stringifier
,
Indent
i
)
{
return
i
;
}
// =======================================================================================
SchemaLoader
schemaLoader
;
...
...
@@ -365,13 +362,13 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
case
schema
:
:
Value
::
Body
::
UINT64_VALUE
:
return
text
(
body
.
getUint64Value
());
case
schema
:
:
Value
::
Body
::
FLOAT32_VALUE
:
return
text
(
body
.
getFloat32Value
());
case
schema
:
:
Value
::
Body
::
FLOAT64_VALUE
:
return
text
(
body
.
getFloat64Value
());
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
return
stringify
(
body
.
getTextValue
(
));
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
stringify
(
body
.
getDataValue
(
));
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
return
text
(
DynamicValue
::
Reader
(
body
.
getTextValue
()
));
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
text
(
DynamicValue
::
Reader
(
body
.
getDataValue
()
));
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
{
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
LIST_TYPE
,
"type/value mismatch"
);
auto
value
=
body
.
getListValue
<
DynamicList
>
(
ListSchema
::
of
(
type
.
getBody
().
getListType
(),
scope
));
return
text
(
stringify
(
value
)
);
return
text
(
value
);
}
case
schema
:
:
Value
::
Body
::
ENUM_VALUE
:
{
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
ENUM_TYPE
,
"type/value mismatch"
);
...
...
@@ -386,7 +383,7 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
,
"type/value mismatch"
);
auto
value
=
body
.
getStructValue
<
DynamicStruct
>
(
scope
.
getDependency
(
type
.
getBody
().
getStructType
()).
asStruct
());
return
text
(
stringify
(
value
)
);
return
text
(
value
);
}
case
schema
:
:
Value
::
Body
::
INTERFACE_VALUE
:
{
return
text
(
""
);
...
...
c++/src/capnproto/dynamic.c++
View file @
36c3f8e9
...
...
@@ -1318,6 +1318,27 @@ DynamicList::Reader DynamicList::Builder::asReader() {
// =======================================================================================
DynamicValue
::
Reader
DynamicValue
::
Builder
::
asReader
()
{
switch
(
type
)
{
case
UNKNOWN
:
return
Reader
();
case
VOID
:
return
Reader
(
voidValue
);
case
BOOL
:
return
Reader
(
boolValue
);
case
INT
:
return
Reader
(
intValue
);
case
UINT
:
return
Reader
(
uintValue
);
case
FLOAT
:
return
Reader
(
floatValue
);
case
TEXT
:
return
Reader
(
textValue
.
asReader
());
case
DATA
:
return
Reader
(
dataValue
.
asReader
());
case
LIST
:
return
Reader
(
listValue
.
asReader
());
case
ENUM
:
return
Reader
(
enumValue
);
case
STRUCT
:
return
Reader
(
structValue
.
asReader
());
case
UNION
:
return
Reader
(
unionValue
.
asReader
());
case
INTERFACE
:
FAIL_CHECK
(
"Interfaces not implemented."
);
return
Reader
();
case
OBJECT
:
return
Reader
(
objectValue
);
}
FAIL_CHECK
(
"Missing switch case."
);
return
Reader
();
}
namespace
{
template
<
typename
T
>
...
...
c++/src/capnproto/dynamic.h
View file @
36c3f8e9
...
...
@@ -191,6 +191,9 @@ private:
:
schema
(
schema
),
reader
(
reader
)
{}
friend
struct
DynamicStruct
;
friend
class
DynamicUnion
::
Builder
;
friend
kj
::
String
internal
::
unionString
(
internal
::
StructReader
reader
,
const
internal
::
RawSchema
&
schema
,
uint
memberIndex
);
};
class
DynamicUnion
::
Builder
{
...
...
@@ -227,6 +230,8 @@ public:
Data
::
Builder
initObjectAsData
(
Text
::
Reader
name
,
uint
size
);
// Convenience methods that identify the member by text name.
Reader
asReader
();
private
:
StructSchema
::
Union
schema
;
internal
::
StructBuilder
builder
;
...
...
@@ -287,7 +292,7 @@ private:
friend
class
MessageBuilder
;
template
<
typename
T
,
::
capnproto
::
Kind
k
>
friend
struct
::
capnproto
::
ToDynamic_
;
friend
kj
::
String
internal
::
debug
String
(
friend
kj
::
String
internal
::
struct
String
(
internal
::
StructReader
reader
,
const
internal
::
RawSchema
&
schema
);
};
...
...
@@ -644,6 +649,17 @@ private:
// specialization. Has a method apply() which does the work.
};
kj
::
String
KJ_STRINGIFY
(
DynamicValue
::
Reader
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicValue
::
Builder
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicEnum
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicObject
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicUnion
::
Reader
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicUnion
::
Builder
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicStruct
::
Reader
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicStruct
::
Builder
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicList
::
Reader
value
);
kj
::
String
KJ_STRINGIFY
(
DynamicList
::
Builder
value
);
// -------------------------------------------------------------------
// Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for
// generated Object accessors.
...
...
@@ -869,6 +885,12 @@ struct DynamicObject::AsImpl<T, Kind::LIST> {
// -------------------------------------------------------------------
inline
DynamicUnion
::
Reader
DynamicUnion
::
Builder
::
asReader
()
{
return
DynamicUnion
::
Reader
(
schema
,
builder
.
asReader
());
}
// -------------------------------------------------------------------
template
<
typename
T
>
typename
T
::
Reader
DynamicStruct
::
Reader
::
as
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
STRUCT
,
...
...
c++/src/capnproto/generated-header-support.h
View file @
36c3f8e9
...
...
@@ -172,13 +172,29 @@ inline const RawSchema& rawSchema() {
template
<
typename
T
>
struct
TypeIdFor
;
kj
::
String
debugString
(
StructReader
reader
,
const
RawSchema
&
schema
);
// Declared here so that we can declare inline debugString() methods on generated types.
template
<
typename
T
>
struct
UnionMemberIndexFor
;
template
<
typename
T
>
inline
uint
unionMemberIndex
()
{
return
UnionMemberIndexFor
<
T
>::
value
;
}
template
<
typename
T
>
struct
UnionParentTypeFor
;
template
<
typename
T
>
using
UnionParentType
=
typename
UnionParentTypeFor
<
T
>::
Type
;
kj
::
String
structString
(
StructReader
reader
,
const
RawSchema
&
schema
);
kj
::
String
unionString
(
StructReader
reader
,
const
RawSchema
&
schema
,
uint
memberIndex
);
// Declared here so that we can declare inline stringify methods on generated types.
// Defined in stringify.c++, which depends on dynamic.c++, which is allowed not to be linked in.
template
<
typename
T
>
inline
kj
::
String
debugString
(
StructReader
reader
)
{
return
debugString
(
reader
,
rawSchema
<
T
>
());
inline
kj
::
String
structString
(
StructReader
reader
)
{
return
structString
(
reader
,
rawSchema
<
T
>
());
}
template
<
typename
T
>
inline
kj
::
String
unionString
(
StructReader
reader
)
{
return
unionString
(
reader
,
rawSchema
<
UnionParentType
<
T
>>
(),
unionMemberIndex
<
T
>
());
}
}
// namespace internal
...
...
@@ -199,6 +215,7 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
#define CAPNPROTO_DEFINE_ENUM(type) \
constexpr Kind KindOf<type>::kind; \
constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_STRUCT(type, id, dataWordSize, pointerCount, preferredElementEncoding) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::STRUCT; }; \
template <> struct StructSizeFor<type> { \
...
...
@@ -213,6 +230,15 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
constexpr Kind KindOf<type>::kind; \
constexpr StructSize StructSizeFor<type>::value; \
constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_UNION(type, parentType, memberIndex) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::UNION; }; \
template <> struct UnionMemberIndexFor<type> { static constexpr uint value = memberIndex; }; \
template <> struct UnionParentTypeFor<type> { typedef parentType Type; }
#define CAPNPROTO_DEFINE_UNION(type) \
constexpr Kind KindOf<type>::kind; \
constexpr uint UnionMemberIndexFor<type>::value;
#define CAPNPROTO_DECLARE_INTERFACE(type, id) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::INTERFACE; }; \
template <> struct TypeIdFor<type> { static constexpr uint64_t typeId = 0x##id; }; \
...
...
c++/src/capnproto/layout.h
View file @
36c3f8e9
...
...
@@ -126,6 +126,7 @@ enum class Kind: uint8_t {
BLOB
,
ENUM
,
STRUCT
,
UNION
,
INTERFACE
,
LIST
,
UNKNOWN
...
...
c++/src/capnproto/schema-loader-test.c++
View file @
36c3f8e9
...
...
@@ -42,8 +42,7 @@ TEST(SchemaLoader, Load) {
Schema
struct8Schema
=
loader
.
load
(
Schema
::
from
<
test
::
TestLists
::
Struct8
>
().
getProto
());
Schema
structPSchema
=
loader
.
load
(
Schema
::
from
<
test
::
TestLists
::
StructP
>
().
getProto
());
EXPECT_STREQ
(
nativeSchema
.
getProto
().
debugString
().
cStr
(),
testListsSchema
.
getProto
().
debugString
().
cStr
());
EXPECT_EQ
(
kj
::
str
(
nativeSchema
.
getProto
()),
kj
::
str
(
testListsSchema
.
getProto
()));
EXPECT_FALSE
(
testListsSchema
==
nativeSchema
);
EXPECT_FALSE
(
struct32Schema
==
Schema
::
from
<
test
::
TestLists
::
Struct32
>
());
...
...
@@ -165,8 +164,8 @@ TEST(SchemaLoader, Upgrade) {
StructSchema
schema
=
loader
.
get
(
typeId
<
test
::
TestOldVersion
>
()).
asStruct
();
EXPECT_
STREQ
(
Schema
::
from
<
test
::
TestOldVersion
>
().
getProto
().
debugString
().
cStr
(
),
schema
.
getProto
().
debugString
().
cStr
(
));
EXPECT_
EQ
(
kj
::
str
(
Schema
::
from
<
test
::
TestOldVersion
>
().
getProto
()
),
kj
::
str
(
schema
.
getProto
()
));
loadUnderAlternateTypeId
<
test
::
TestNewVersion
>
(
loader
,
typeId
<
test
::
TestOldVersion
>
());
...
...
@@ -185,8 +184,7 @@ TEST(SchemaLoader, Downgrade) {
StructSchema
schema
=
loader
.
get
(
typeId
<
test
::
TestNewVersion
>
()).
asStruct
();
EXPECT_STREQ
(
Schema
::
from
<
test
::
TestNewVersion
>
().
getProto
().
debugString
().
cStr
(),
schema
.
getProto
().
debugString
().
cStr
());
EXPECT_EQ
(
kj
::
str
(
Schema
::
from
<
test
::
TestNewVersion
>
().
getProto
()),
kj
::
str
(
schema
.
getProto
()));
loadUnderAlternateTypeId
<
test
::
TestOldVersion
>
(
loader
,
typeId
<
test
::
TestNewVersion
>
());
...
...
c++/src/capnproto/schema.h
View file @
36c3f8e9
...
...
@@ -124,8 +124,10 @@ private:
return
StructSchema
(
&
internal
::
rawSchema
<
T
>
());
}
friend
class
Schema
;
friend
kj
::
String
internal
::
debug
String
(
friend
kj
::
String
internal
::
struct
String
(
internal
::
StructReader
reader
,
const
internal
::
RawSchema
&
schema
);
friend
kj
::
String
internal
::
unionString
(
internal
::
StructReader
reader
,
const
internal
::
RawSchema
&
schema
,
uint
memberIndex
);
};
class
StructSchema
::
Member
{
...
...
c++/src/capnproto/stringify-test.c++
View file @
36c3f8e9
...
...
@@ -45,12 +45,18 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "stringify.h"
#include "message.h"
#include "dynamic.h"
#include <kj/logging.h>
#include <gtest/gtest.h>
#include "test-util.h"
namespace
kj
{
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
kj
::
String
&
s
)
{
return
os
.
write
(
s
.
begin
(),
s
.
size
());
}
}
namespace
capnproto
{
namespace
internal
{
namespace
{
...
...
@@ -59,11 +65,11 @@ TEST(Stringify, DebugString) {
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
TestAllTypes
>
();
EXPECT_
STREQ
(
"()"
,
root
.
debugString
().
cStr
(
));
EXPECT_
EQ
(
"()"
,
kj
::
str
(
root
));
initTestMessage
(
root
);
EXPECT_
STR
EQ
(
"("
EXPECT_EQ
(
"("
"boolField = true, "
"int8Field = -123, "
"int16Field = -12345, "
...
...
@@ -134,17 +140,39 @@ TEST(Stringify, DebugString) {
"(textField =
\"
structlist 2
\"
), "
"(textField =
\"
structlist 3
\"
)], "
"enumList = [foo, garply])"
,
root
.
debugString
().
cStr
());
kj
::
str
(
root
));
}
TEST
(
Stringify
,
Unions
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
TestUnion
>
();
root
.
getUnion0
().
setU0f0s16
(
321
);
root
.
getUnion1
().
setU1f0sp
(
"foo"
);
root
.
getUnion2
().
setU2f0s1
(
true
);
root
.
getUnion3
().
setU3f0s64
(
123456789012345678ll
);
EXPECT_EQ
(
"("
"union0 = u0f0s16(321), "
"union1 = u1f0sp(
\"
foo
\"
), "
"union2 = u2f0s1(true), "
"union3 = u3f0s64(123456789012345678))"
,
kj
::
str
(
root
));
EXPECT_EQ
(
"u0f0s16(321)"
,
kj
::
str
(
root
.
getUnion0
()));
EXPECT_EQ
(
"u1f0sp(
\"
foo
\"
)"
,
kj
::
str
(
root
.
getUnion1
()));
EXPECT_EQ
(
"u2f0s1(true)"
,
kj
::
str
(
root
.
getUnion2
()));
EXPECT_EQ
(
"u3f0s64(123456789012345678)"
,
kj
::
str
(
root
.
getUnion3
()));
}
TEST
(
Stringify
,
MoreValues
)
{
EXPECT_
STREQ
(
"123"
,
stringify
(
123
).
cStr
(
));
EXPECT_
STREQ
(
"1.23e47"
,
stringify
(
123e45
).
cStr
(
));
EXPECT_
STREQ
(
"
\"
foo
\"
"
,
stringify
(
"foo"
).
cStr
(
));
EXPECT_
STREQ
(
"
\"\\
a
\\
b
\\
n
\\
t
\\\"\"
"
,
stringify
(
"
\a\b\n\t\"
"
).
cStr
(
));
EXPECT_
EQ
(
"123"
,
kj
::
str
(
DynamicValue
::
Reader
(
123
)
));
EXPECT_
EQ
(
"1.23e47"
,
kj
::
str
(
DynamicValue
::
Reader
(
123e45
)
));
EXPECT_
EQ
(
"
\"
foo
\"
"
,
kj
::
str
(
DynamicValue
::
Reader
(
"foo"
)
));
EXPECT_
EQ
(
"
\"\\
a
\\
b
\\
n
\\
t
\\\"\"
"
,
kj
::
str
(
DynamicValue
::
Reader
(
"
\a\b\n\t\"
"
)
));
EXPECT_
STREQ
(
"foo"
,
stringify
(
TestEnum
::
FOO
).
cStr
(
));
EXPECT_
STREQ
(
"123"
,
stringify
(
static_cast
<
TestEnum
>
(
123
)).
cStr
(
));
EXPECT_
EQ
(
"foo"
,
kj
::
str
(
DynamicValue
::
Reader
(
TestEnum
::
FOO
)
));
EXPECT_
EQ
(
"123"
,
kj
::
str
(
DynamicValue
::
Reader
(
static_cast
<
TestEnum
>
(
123
))
));
}
}
// namespace
...
...
c++/src/capnproto/stringify.c++
View file @
36c3f8e9
...
...
@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "
stringify
.h"
#include "
dynamic
.h"
#include <kj/logging.h>
#include <sstream>
...
...
@@ -58,10 +58,10 @@ static void print(std::ostream& os, DynamicValue::Reader value,
break
;
case
DynamicValue
:
:
FLOAT
:
{
if
(
which
==
schema
::
Type
::
Body
::
FLOAT32_TYPE
)
{
auto
buf
=
kj
::
STR
*
value
.
as
<
float
>
(
);
auto
buf
=
kj
::
toCharSequence
(
value
.
as
<
float
>
()
);
os
.
write
(
buf
.
begin
(),
buf
.
size
());
}
else
{
auto
buf
=
kj
::
STR
*
value
.
as
<
double
>
(
);
auto
buf
=
kj
::
toCharSequence
(
value
.
as
<
double
>
()
);
os
.
write
(
buf
.
begin
(),
buf
.
size
());
}
break
;
...
...
@@ -170,8 +170,6 @@ static void print(std::ostream& os, DynamicValue::Reader value,
}
}
}
// namespace
kj
::
String
stringify
(
DynamicValue
::
Reader
value
)
{
std
::
stringstream
out
;
print
(
out
,
value
,
schema
::
Type
::
Body
::
STRUCT_TYPE
);
...
...
@@ -179,12 +177,30 @@ kj::String stringify(DynamicValue::Reader value) {
return
kj
::
heapString
(
content
.
data
(),
content
.
size
());
}
}
// namespace
kj
::
String
KJ_STRINGIFY
(
DynamicValue
::
Reader
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicValue
::
Builder
value
)
{
return
stringify
(
value
.
asReader
());
}
kj
::
String
KJ_STRINGIFY
(
DynamicEnum
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicObject
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicUnion
::
Reader
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicUnion
::
Builder
value
)
{
return
stringify
(
value
.
asReader
());
}
kj
::
String
KJ_STRINGIFY
(
DynamicStruct
::
Reader
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicStruct
::
Builder
value
)
{
return
stringify
(
value
.
asReader
());
}
kj
::
String
KJ_STRINGIFY
(
DynamicList
::
Reader
value
)
{
return
stringify
(
value
);
}
kj
::
String
KJ_STRINGIFY
(
DynamicList
::
Builder
value
)
{
return
stringify
(
value
.
asReader
());
}
namespace
internal
{
kj
::
String
debug
String
(
StructReader
reader
,
const
RawSchema
&
schema
)
{
kj
::
String
struct
String
(
StructReader
reader
,
const
RawSchema
&
schema
)
{
return
stringify
(
DynamicStruct
::
Reader
(
StructSchema
(
&
schema
),
reader
));
}
kj
::
String
unionString
(
StructReader
reader
,
const
RawSchema
&
schema
,
uint
memberIndex
)
{
return
stringify
(
DynamicUnion
::
Reader
(
StructSchema
(
&
schema
).
getMembers
()[
memberIndex
].
asUnion
(),
reader
));
}
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/stringify.h
deleted
100644 → 0
View file @
47194adc
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_STRINGIFY_H_
#define CAPNPROTO_STRINGIFY_H_
#include "dynamic.h"
namespace
capnproto
{
kj
::
String
stringify
(
DynamicValue
::
Reader
value
);
// Stringify an arbitrary Cap'n Proto value. Note that DynamicValue::Reader can be implicitly
// constructed from any Cap'n Proto field type, so this will accept pretty much anything.
}
// namespace capnproto
#endif // CAPNPROTO_STRINGIFY_H_
c++/src/capnproto/test.capnp
View file @
36c3f8e9
...
...
@@ -353,6 +353,12 @@ struct TestLateUnion {
corge @5 :List(Int32);
grault @6 :Float32;
}
anotherUnion @7 union {
qux @8 :Text;
corge @9 :List(Int32);
grault @10 :Float32;
}
}
struct TestOldVersion {
...
...
c++/src/kj/common.h
View file @
36c3f8e9
...
...
@@ -189,6 +189,14 @@ template <typename T> struct Decay_<const T> { typedef typename Decay_<T>::Type
template
<
typename
T
>
struct
Decay_
<
volatile
T
>
{
typedef
typename
Decay_
<
T
>::
Type
Type
;
};
template
<
typename
T
>
using
Decay
=
typename
Decay_
<
T
>::
Type
;
template
<
bool
b
>
struct
EnableIf_
;
template
<>
struct
EnableIf_
<
true
>
{
typedef
void
Type
;
};
template
<
bool
b
>
using
EnableIf
=
typename
EnableIf_
<
b
>::
Type
;
// Use like:
//
// template <typename T, typename = EnableIf<isValid<T>()>
// void func(T&& t);
template
<
typename
T
>
T
instance
()
noexcept
;
// Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify
...
...
c++/src/kj/exception.c++
View file @
36c3f8e9
...
...
@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "exception.h"
#include "
util
.h"
#include "
string
.h"
#include "logging.h"
#include <unistd.h>
#include <execinfo.h>
...
...
@@ -30,7 +30,7 @@
namespace
kj
{
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Exception
::
Nature
nature
)
{
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Exception
::
Nature
nature
)
{
static
const
char
*
NATURE_STRINGS
[]
=
{
"precondition not met"
,
"bug in code"
,
...
...
@@ -44,7 +44,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
return
arrayPtr
(
s
,
strlen
(
s
));
}
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Exception
::
Durability
durability
)
{
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Exception
::
Durability
durability
)
{
static
const
char
*
DURABILITY_STRINGS
[]
=
{
"temporary"
,
"permanent"
...
...
@@ -149,7 +149,7 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) {
if
(
std
::
uncaught_exception
())
{
logMessage
(
str
(
"unwind: "
,
exception
.
what
(),
'\n'
));
}
else
{
throw
std
::
move
(
exception
);
throw
kj
::
mv
(
exception
);
}
#endif
}
...
...
@@ -158,7 +158,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS
logMessage
(
str
(
exception
.
what
(),
'\n'
));
#else
throw
std
::
move
(
exception
);
throw
kj
::
mv
(
exception
);
#endif
}
...
...
c++/src/kj/exception.h
View file @
36c3f8e9
...
...
@@ -116,9 +116,8 @@ private:
mutable
String
whatBuffer
;
};
struct
Stringifier
;
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Exception
::
Nature
nature
);
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Exception
::
Durability
durability
);
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Exception
::
Nature
nature
);
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Exception
::
Durability
durability
);
class
ExceptionCallback
{
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
...
...
c++/src/kj/logging.c++
View file @
36c3f8e9
...
...
@@ -31,7 +31,7 @@ namespace kj {
Log
::
Severity
Log
::
minSeverity
=
Log
::
Severity
::
INFO
;
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Log
::
Severity
severity
)
{
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Log
::
Severity
severity
)
{
static
const
char
*
SEVERITY_STRINGS
[]
=
{
"info"
,
"warning"
,
...
...
@@ -163,20 +163,20 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
case
LOG
:
break
;
case
ASSERTION
:
pos
=
fill
(
pos
,
expected
,
codeArray
);
pos
=
internal
::
fill
(
pos
,
expected
,
codeArray
);
break
;
case
SYSCALL
:
pos
=
fill
(
pos
,
codeArray
,
colon
,
sysErrorArray
);
pos
=
internal
::
fill
(
pos
,
codeArray
,
colon
,
sysErrorArray
);
break
;
}
for
(
size_t
i
=
0
;
i
<
argValues
.
size
();
i
++
)
{
if
(
i
>
0
||
style
!=
LOG
)
{
pos
=
fill
(
pos
,
delim
);
pos
=
internal
::
fill
(
pos
,
delim
);
}
if
(
argNames
[
i
].
size
()
>
0
&&
argNames
[
i
][
0
]
!=
'\"'
)
{
pos
=
fill
(
pos
,
argNames
[
i
],
sep
);
pos
=
internal
::
fill
(
pos
,
argNames
[
i
],
sep
);
}
pos
=
fill
(
pos
,
argValues
[
i
]);
pos
=
internal
::
fill
(
pos
,
argValues
[
i
]);
}
return
result
;
...
...
c++/src/kj/logging.h
View file @
36c3f8e9
...
...
@@ -96,7 +96,7 @@
#ifndef KJ_LOGGING_H_
#define KJ_LOGGING_H_
#include "
util
.h"
#include "
string
.h"
#include "exception.h"
namespace
kj
{
...
...
@@ -208,7 +208,7 @@ private:
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
};
ArrayPtr
<
const
char
>
operator
*
(
const
Stringifier
&
,
Log
::
Severity
severity
);
ArrayPtr
<
const
char
>
KJ_STRINGIFY
(
Log
::
Severity
severity
);
#define LOG(severity, ...) \
if (!::kj::Log::shouldLog(::kj::Log::Severity::severity)) {} else \
...
...
c++/src/kj/
util
-test.c++
→
c++/src/kj/
string
-test.c++
View file @
36c3f8e9
...
...
@@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "
util
.h"
#include "
string
.h"
#include <gtest/gtest.h>
#include <string>
...
...
@@ -29,7 +29,7 @@ namespace kj {
namespace
internal
{
namespace
{
TEST
(
Util
,
Foo
)
{
TEST
(
String
,
Str
)
{
EXPECT_EQ
(
"foobar"
,
str
(
"foo"
,
"bar"
));
EXPECT_EQ
(
"1 2 3 4"
,
str
(
1
,
" "
,
2u
,
" "
,
3l
,
" "
,
4ll
));
EXPECT_EQ
(
"1.5 foo 1e15 bar -3"
,
str
(
1.5
f
,
" foo "
,
1e15
,
" bar "
,
-
3
));
...
...
c++/src/kj/string.c++
View file @
36c3f8e9
...
...
@@ -23,6 +23,11 @@
#include "string.h"
#include "logging.h"
#include <stdio.h>
#include <float.h>
#include <limits>
#include <errno.h>
#include <stdlib.h>
namespace
kj
{
...
...
@@ -38,4 +43,280 @@ String heapString(const char* value, size_t size) {
return
String
(
buffer
,
size
,
internal
::
HeapArrayDisposer
::
instance
);
}
#define HEXIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> hex(type i) { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
HEXIFY_INT
(
unsigned
short
,
"%x"
);
HEXIFY_INT
(
unsigned
int
,
"%x"
);
HEXIFY_INT
(
unsigned
long
,
"%lx"
);
HEXIFY_INT
(
unsigned
long
long
,
"%llx"
);
#undef HEXIFY_INT
namespace
internal
{
StringPtr
Stringifier
::
operator
*
(
bool
b
)
const
{
return
b
?
StringPtr
(
"true"
)
:
StringPtr
(
"false"
);
}
#define STRINGIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> Stringifier::operator*(type i) const { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
STRINGIFY_INT
(
short
,
"%d"
);
STRINGIFY_INT
(
unsigned
short
,
"%u"
);
STRINGIFY_INT
(
int
,
"%d"
);
STRINGIFY_INT
(
unsigned
int
,
"%u"
);
STRINGIFY_INT
(
long
,
"%ld"
);
STRINGIFY_INT
(
unsigned
long
,
"%lu"
);
STRINGIFY_INT
(
long
long
,
"%lld"
);
STRINGIFY_INT
(
unsigned
long
long
,
"%llu"
);
STRINGIFY_INT
(
const
void
*
,
"%p"
);
#undef STRINGIFY_INT
namespace
{
// ----------------------------------------------------------------------
// DoubleToBuffer()
// FloatToBuffer()
// Copied from Protocol Buffers, (C) Google, BSD license.
// Kenton wrote this code originally. The following commentary is
// from the original.
//
// Description: converts a double or float to a string which, if
// passed to NoLocaleStrtod(), will produce the exact same original double
// (except in case of NaN; all NaNs are considered the same value).
// We try to keep the string short but it's not guaranteed to be as
// short as possible.
//
// DoubleToBuffer() and FloatToBuffer() write the text to the given
// buffer and return it. The buffer must be at least
// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
// bytes for floats. kFastToBufferSize is also guaranteed to be large
// enough to hold either.
//
// We want to print the value without losing precision, but we also do
// not want to print more digits than necessary. This turns out to be
// trickier than it sounds. Numbers like 0.2 cannot be represented
// exactly in binary. If we print 0.2 with a very large precision,
// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
// On the other hand, if we set the precision too low, we lose
// significant digits when printing numbers that actually need them.
// It turns out there is no precision value that does the right thing
// for all numbers.
//
// Our strategy is to first try printing with a precision that is never
// over-precise, then parse the result with strtod() to see if it
// matches. If not, we print again with a precision that will always
// give a precise result, but may use more digits than necessary.
//
// An arguably better strategy would be to use the algorithm described
// in "How to Print Floating-Point Numbers Accurately" by Steele &
// White, e.g. as implemented by David M. Gay's dtoa(). It turns out,
// however, that the following implementation is about as fast as
// DMG's code. Furthermore, DMG's code locks mutexes, which means it
// will not scale well on multi-core machines. DMG's code is slightly
// more accurate (in that it will never use more digits than
// necessary), but this is probably irrelevant for most users.
//
// Rob Pike and Ken Thompson also have an implementation of dtoa() in
// third_party/fmt/fltfmt.cc. Their implementation is similar to this
// one in that it makes guesses and then uses strtod() to check them.
// Their implementation is faster because they use their own code to
// generate the digits in the first place rather than use snprintf(),
// thus avoiding format string parsing overhead. However, this makes
// it considerably more complicated than the following implementation,
// and it is embedded in a larger library. If speed turns out to be
// an issue, we could re-implement this in terms of their
// implementation.
// ----------------------------------------------------------------------
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
// MinGW has both snprintf and _snprintf, but they appear to be different
// functions. The former is buggy. When invoked like so:
// char buffer[32];
// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f);
// it prints "1.23000e+10". This is plainly wrong: %g should never print
// trailing zeros after the decimal point. For some reason this bug only
// occurs with some input values, not all. In any case, _snprintf does the
// right thing, so we use it.
#define snprintf _snprintf
#endif
inline
bool
IsNaN
(
double
value
)
{
// NaN is never equal to anything, even itself.
return
value
!=
value
;
}
// In practice, doubles should never need more than 24 bytes and floats
// should never need more than 14 (including null terminators), but we
// overestimate to be safe.
static
const
int
kDoubleToBufferSize
=
32
;
static
const
int
kFloatToBufferSize
=
24
;
static
inline
bool
IsValidFloatChar
(
char
c
)
{
return
(
'0'
<=
c
&&
c
<=
'9'
)
||
c
==
'e'
||
c
==
'E'
||
c
==
'+'
||
c
==
'-'
;
}
void
DelocalizeRadix
(
char
*
buffer
)
{
// Fast check: if the buffer has a normal decimal point, assume no
// translation is needed.
if
(
strchr
(
buffer
,
'.'
)
!=
NULL
)
return
;
// Find the first unknown character.
while
(
IsValidFloatChar
(
*
buffer
))
++
buffer
;
if
(
*
buffer
==
'\0'
)
{
// No radix character found.
return
;
}
// We are now pointing at the locale-specific radix character. Replace it
// with '.'.
*
buffer
=
'.'
;
++
buffer
;
if
(
!
IsValidFloatChar
(
*
buffer
)
&&
*
buffer
!=
'\0'
)
{
// It appears the radix was a multi-byte character. We need to remove the
// extra bytes.
char
*
target
=
buffer
;
do
{
++
buffer
;
}
while
(
!
IsValidFloatChar
(
*
buffer
)
&&
*
buffer
!=
'\0'
);
memmove
(
target
,
buffer
,
strlen
(
buffer
)
+
1
);
}
}
void
RemovePlus
(
char
*
buffer
)
{
// Remove any + characters because they are redundant and ugly.
for
(;;)
{
buffer
=
strchr
(
buffer
,
'+'
);
if
(
buffer
==
NULL
)
{
return
;
}
memmove
(
buffer
,
buffer
+
1
,
strlen
(
buffer
+
1
)
+
1
);
}
}
char
*
DoubleToBuffer
(
double
value
,
char
*
buffer
)
{
// DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
// platforms these days. Just in case some system exists where DBL_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert
(
DBL_DIG
<
20
,
"DBL_DIG is too big."
);
if
(
value
==
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"inf"
);
return
buffer
;
}
else
if
(
value
==
-
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"-inf"
);
return
buffer
;
}
else
if
(
IsNaN
(
value
))
{
strcpy
(
buffer
,
"nan"
);
return
buffer
;
}
int
snprintf_result
=
snprintf
(
buffer
,
kDoubleToBufferSize
,
"%.*g"
,
DBL_DIG
,
value
);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kDoubleToBufferSize
);
// We need to make parsed_value volatile in order to force the compiler to
// write it out to the stack. Otherwise, it may keep the value in a
// register, and if it does that, it may keep it as a long double instead
// of a double. This long double may have extra bits that make it compare
// unequal to "value" even though it would be exactly equal if it were
// truncated to a double.
volatile
double
parsed_value
=
strtod
(
buffer
,
NULL
);
if
(
parsed_value
!=
value
)
{
int
snprintf_result
=
snprintf
(
buffer
,
kDoubleToBufferSize
,
"%.*g"
,
DBL_DIG
+
2
,
value
);
// Should never overflow; see above.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kDoubleToBufferSize
);
}
DelocalizeRadix
(
buffer
);
RemovePlus
(
buffer
);
return
buffer
;
}
bool
safe_strtof
(
const
char
*
str
,
float
*
value
)
{
char
*
endptr
;
errno
=
0
;
// errno only gets set on errors
#if defined(_WIN32) || defined (__hpux) // has no strtof()
*
value
=
strtod
(
str
,
&
endptr
);
#else
*
value
=
strtof
(
str
,
&
endptr
);
#endif
return
*
str
!=
0
&&
*
endptr
==
0
&&
errno
==
0
;
}
char
*
FloatToBuffer
(
float
value
,
char
*
buffer
)
{
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert
(
FLT_DIG
<
10
,
"FLT_DIG is too big"
);
if
(
value
==
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"inf"
);
return
buffer
;
}
else
if
(
value
==
-
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"-inf"
);
return
buffer
;
}
else
if
(
IsNaN
(
value
))
{
strcpy
(
buffer
,
"nan"
);
return
buffer
;
}
int
snprintf_result
=
snprintf
(
buffer
,
kFloatToBufferSize
,
"%.*g"
,
FLT_DIG
,
value
);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kFloatToBufferSize
);
float
parsed_value
;
if
(
!
safe_strtof
(
buffer
,
&
parsed_value
)
||
parsed_value
!=
value
)
{
int
snprintf_result
=
snprintf
(
buffer
,
kFloatToBufferSize
,
"%.*g"
,
FLT_DIG
+
2
,
value
);
// Should never overflow; see above.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kFloatToBufferSize
);
}
DelocalizeRadix
(
buffer
);
RemovePlus
(
buffer
);
return
buffer
;
}
}
// namespace
CappedArray
<
char
,
kFloatToBufferSize
>
Stringifier
::
operator
*
(
float
f
)
const
{
CappedArray
<
char
,
kFloatToBufferSize
>
result
;
result
.
setSize
(
strlen
(
FloatToBuffer
(
f
,
result
.
begin
())));
return
result
;
}
CappedArray
<
char
,
kDoubleToBufferSize
>
Stringifier
::
operator
*
(
double
f
)
const
{
CappedArray
<
char
,
kDoubleToBufferSize
>
result
;
result
.
setSize
(
strlen
(
DoubleToBuffer
(
f
,
result
.
begin
())));
return
result
;
}
}
// namespace internal
}
// namespace kj
c++/src/kj/string.h
View file @
36c3f8e9
...
...
@@ -24,6 +24,7 @@
#ifndef KJ_STRING_H_
#define KJ_STRING_H_
#include <initializer_list>
#include "array.h"
#include <string.h>
...
...
@@ -140,6 +141,177 @@ String heapString(StringPtr value);
String
heapString
(
ArrayPtr
<
const
char
>
value
);
// Allocates a copy of the given value on the heap.
// =======================================================================================
// Magic str() function which transforms parameters to text and concatenates them into one big
// String.
namespace
internal
{
inline
size_t
sum
(
std
::
initializer_list
<
size_t
>
nums
)
{
size_t
result
=
0
;
for
(
auto
num
:
nums
)
{
result
+=
num
;
}
return
result
;
}
inline
char
*
fill
(
char
*
ptr
)
{
return
ptr
;
}
template
<
typename
First
,
typename
...
Rest
>
char
*
fill
(
char
*
__restrict__
target
,
const
First
&
first
,
Rest
&&
...
rest
)
{
auto
i
=
first
.
begin
();
auto
end
=
first
.
end
();
while
(
i
!=
end
)
{
*
target
++
=
*
i
++
;
}
return
fill
(
target
,
kj
::
fwd
<
Rest
>
(
rest
)...);
}
template
<
typename
...
Params
>
String
concat
(
Params
&&
...
params
)
{
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `char`.
String
result
=
heapString
(
sum
({
params
.
size
()...}));
fill
(
result
.
begin
(),
kj
::
fwd
<
Params
>
(
params
)...);
return
result
;
}
inline
String
concat
(
String
&&
arr
)
{
return
kj
::
mv
(
arr
);
}
struct
Stringifier
{
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline
ArrayPtr
<
const
char
>
operator
*
(
ArrayPtr
<
const
char
>
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
Array
<
const
char
>&
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
Array
<
char
>&
s
)
const
{
return
s
;
}
template
<
size_t
n
>
inline
ArrayPtr
<
const
char
>
operator
*
(
const
CappedArray
<
char
,
n
>&
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
char
*
s
)
const
{
return
arrayPtr
(
s
,
strlen
(
s
));
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
String
&
s
)
const
{
return
s
.
asArray
();
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
StringPtr
&
s
)
const
{
return
s
.
asArray
();
}
inline
FixedArray
<
char
,
1
>
operator
*
(
char
c
)
const
{
FixedArray
<
char
,
1
>
result
;
result
[
0
]
=
c
;
return
result
;
}
StringPtr
operator
*
(
bool
b
)
const
;
CappedArray
<
char
,
sizeof
(
short
)
*
4
>
operator
*
(
short
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
short
)
*
4
>
operator
*
(
unsigned
short
i
)
const
;
CappedArray
<
char
,
sizeof
(
int
)
*
4
>
operator
*
(
int
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
int
)
*
4
>
operator
*
(
unsigned
int
i
)
const
;
CappedArray
<
char
,
sizeof
(
long
)
*
4
>
operator
*
(
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
long
)
*
4
>
operator
*
(
unsigned
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
long
long
)
*
4
>
operator
*
(
long
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
long
long
)
*
4
>
operator
*
(
unsigned
long
long
i
)
const
;
CappedArray
<
char
,
24
>
operator
*
(
float
f
)
const
;
CappedArray
<
char
,
32
>
operator
*
(
double
f
)
const
;
CappedArray
<
char
,
sizeof
(
const
void
*
)
*
4
>
operator
*
(
const
void
*
s
)
const
;
template
<
typename
T
>
Array
<
char
>
operator
*
(
ArrayPtr
<
T
>
arr
)
const
;
template
<
typename
T
>
Array
<
char
>
operator
*
(
const
Array
<
T
>&
arr
)
const
;
};
static
constexpr
Stringifier
STR
=
Stringifier
();
}
// namespace internal
template
<
typename
T
>
auto
toCharSequence
(
T
&&
value
)
->
decltype
(
internal
::
STR
*
kj
::
fwd
<
T
>
(
value
))
{
// Returns an iterable of chars that represent a textual representation of the value, suitable
// for debugging.
//
// Most users should use str() instead, but toCharSequence() may occasionally be useful to avoid
// heap allocation overhead that str() implies.
//
// To specialize this function for your type, see KJ_STRINGIFY.
return
internal
::
STR
*
kj
::
fwd
<
T
>
(
value
);
}
CappedArray
<
char
,
sizeof
(
unsigned
short
)
*
4
>
hex
(
unsigned
short
i
);
CappedArray
<
char
,
sizeof
(
unsigned
int
)
*
4
>
hex
(
unsigned
int
i
);
CappedArray
<
char
,
sizeof
(
unsigned
long
)
*
4
>
hex
(
unsigned
long
i
);
CappedArray
<
char
,
sizeof
(
unsigned
long
long
)
*
4
>
hex
(
unsigned
long
long
i
);
template
<
typename
...
Params
>
String
str
(
Params
&&
...
params
)
{
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return
internal
::
concat
(
toCharSequence
(
kj
::
fwd
<
Params
>
(
params
))...);
}
inline
String
str
(
String
&&
s
)
{
return
mv
(
s
);
}
// Overload to prevent redundant allocation.
template
<
typename
T
>
String
strArray
(
T
&&
arr
,
const
char
*
delim
)
{
size_t
delimLen
=
strlen
(
delim
);
KJ_STACK_ARRAY
(
decltype
(
internal
::
STR
*
arr
[
0
]),
pieces
,
arr
.
size
(),
8
,
32
);
size_t
size
=
0
;
for
(
size_t
i
=
0
;
i
<
arr
.
size
();
i
++
)
{
if
(
i
>
0
)
size
+=
delimLen
;
pieces
[
i
]
=
internal
::
STR
*
arr
[
i
];
size
+=
pieces
[
i
].
size
();
}
String
result
=
heapString
(
size
);
char
*
pos
=
result
.
begin
();
for
(
size_t
i
=
0
;
i
<
arr
.
size
();
i
++
)
{
if
(
i
>
0
)
{
memcpy
(
pos
,
delim
,
delimLen
);
pos
+=
delimLen
;
}
pos
=
internal
::
fill
(
pos
,
pieces
[
i
]);
}
return
result
;
}
namespace
internal
{
template
<
typename
T
>
inline
Array
<
char
>
Stringifier
::
operator
*
(
ArrayPtr
<
T
>
arr
)
const
{
return
strArray
(
arr
,
", "
);
}
template
<
typename
T
>
inline
Array
<
char
>
Stringifier
::
operator
*
(
const
Array
<
T
>&
arr
)
const
{
return
strArray
(
arr
,
", "
);
}
}
// namespace internal
#define KJ_STRINGIFY(...) operator*(::kj::internal::Stringifier, __VA_ARGS__)
// Defines a stringifier for a custom type. Example:
//
// class Foo {...};
// inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); }
//
// This allows Foo to be passed to str().
//
// The function should be declared either in the same namespace as the target type or in the global
// namespace. It can return any type which is an iterable container of chars.
// =======================================================================================
// Inline implementation details.
...
...
c++/src/kj/util.c++
deleted
100644 → 0
View file @
47194adc
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h"
#include "logging.h"
#include <stdio.h>
#include <float.h>
#include <limits>
#include <errno.h>
#include <stdlib.h>
namespace
kj
{
#define STRINGIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> Stringifier::operator*(type i) const { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
STRINGIFY_INT
(
short
,
"%d"
);
STRINGIFY_INT
(
unsigned
short
,
"%u"
);
STRINGIFY_INT
(
int
,
"%d"
);
STRINGIFY_INT
(
unsigned
int
,
"%u"
);
STRINGIFY_INT
(
long
,
"%ld"
);
STRINGIFY_INT
(
unsigned
long
,
"%lu"
);
STRINGIFY_INT
(
long
long
,
"%lld"
);
STRINGIFY_INT
(
unsigned
long
long
,
"%llu"
);
STRINGIFY_INT
(
const
void
*
,
"%p"
);
#undef STRINGIFY_INT
#define HEXIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> hex(type i) { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
HEXIFY_INT
(
unsigned
short
,
"%x"
);
HEXIFY_INT
(
unsigned
int
,
"%x"
);
HEXIFY_INT
(
unsigned
long
,
"%lx"
);
HEXIFY_INT
(
unsigned
long
long
,
"%llx"
);
#undef HEXIFY_INT
namespace
{
// ----------------------------------------------------------------------
// DoubleToBuffer()
// FloatToBuffer()
// Copied from Protocol Buffers, (C) Google, BSD license.
// Kenton wrote this code originally. The following commentary is
// from the original.
//
// Description: converts a double or float to a string which, if
// passed to NoLocaleStrtod(), will produce the exact same original double
// (except in case of NaN; all NaNs are considered the same value).
// We try to keep the string short but it's not guaranteed to be as
// short as possible.
//
// DoubleToBuffer() and FloatToBuffer() write the text to the given
// buffer and return it. The buffer must be at least
// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
// bytes for floats. kFastToBufferSize is also guaranteed to be large
// enough to hold either.
//
// We want to print the value without losing precision, but we also do
// not want to print more digits than necessary. This turns out to be
// trickier than it sounds. Numbers like 0.2 cannot be represented
// exactly in binary. If we print 0.2 with a very large precision,
// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
// On the other hand, if we set the precision too low, we lose
// significant digits when printing numbers that actually need them.
// It turns out there is no precision value that does the right thing
// for all numbers.
//
// Our strategy is to first try printing with a precision that is never
// over-precise, then parse the result with strtod() to see if it
// matches. If not, we print again with a precision that will always
// give a precise result, but may use more digits than necessary.
//
// An arguably better strategy would be to use the algorithm described
// in "How to Print Floating-Point Numbers Accurately" by Steele &
// White, e.g. as implemented by David M. Gay's dtoa(). It turns out,
// however, that the following implementation is about as fast as
// DMG's code. Furthermore, DMG's code locks mutexes, which means it
// will not scale well on multi-core machines. DMG's code is slightly
// more accurate (in that it will never use more digits than
// necessary), but this is probably irrelevant for most users.
//
// Rob Pike and Ken Thompson also have an implementation of dtoa() in
// third_party/fmt/fltfmt.cc. Their implementation is similar to this
// one in that it makes guesses and then uses strtod() to check them.
// Their implementation is faster because they use their own code to
// generate the digits in the first place rather than use snprintf(),
// thus avoiding format string parsing overhead. However, this makes
// it considerably more complicated than the following implementation,
// and it is embedded in a larger library. If speed turns out to be
// an issue, we could re-implement this in terms of their
// implementation.
// ----------------------------------------------------------------------
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
// MinGW has both snprintf and _snprintf, but they appear to be different
// functions. The former is buggy. When invoked like so:
// char buffer[32];
// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f);
// it prints "1.23000e+10". This is plainly wrong: %g should never print
// trailing zeros after the decimal point. For some reason this bug only
// occurs with some input values, not all. In any case, _snprintf does the
// right thing, so we use it.
#define snprintf _snprintf
#endif
inline
bool
IsNaN
(
double
value
)
{
// NaN is never equal to anything, even itself.
return
value
!=
value
;
}
// In practice, doubles should never need more than 24 bytes and floats
// should never need more than 14 (including null terminators), but we
// overestimate to be safe.
static
const
int
kDoubleToBufferSize
=
32
;
static
const
int
kFloatToBufferSize
=
24
;
static
inline
bool
IsValidFloatChar
(
char
c
)
{
return
(
'0'
<=
c
&&
c
<=
'9'
)
||
c
==
'e'
||
c
==
'E'
||
c
==
'+'
||
c
==
'-'
;
}
void
DelocalizeRadix
(
char
*
buffer
)
{
// Fast check: if the buffer has a normal decimal point, assume no
// translation is needed.
if
(
strchr
(
buffer
,
'.'
)
!=
NULL
)
return
;
// Find the first unknown character.
while
(
IsValidFloatChar
(
*
buffer
))
++
buffer
;
if
(
*
buffer
==
'\0'
)
{
// No radix character found.
return
;
}
// We are now pointing at the locale-specific radix character. Replace it
// with '.'.
*
buffer
=
'.'
;
++
buffer
;
if
(
!
IsValidFloatChar
(
*
buffer
)
&&
*
buffer
!=
'\0'
)
{
// It appears the radix was a multi-byte character. We need to remove the
// extra bytes.
char
*
target
=
buffer
;
do
{
++
buffer
;
}
while
(
!
IsValidFloatChar
(
*
buffer
)
&&
*
buffer
!=
'\0'
);
memmove
(
target
,
buffer
,
strlen
(
buffer
)
+
1
);
}
}
void
RemovePlus
(
char
*
buffer
)
{
// Remove any + characters because they are redundant and ugly.
for
(;;)
{
buffer
=
strchr
(
buffer
,
'+'
);
if
(
buffer
==
NULL
)
{
return
;
}
memmove
(
buffer
,
buffer
+
1
,
strlen
(
buffer
+
1
)
+
1
);
}
}
char
*
DoubleToBuffer
(
double
value
,
char
*
buffer
)
{
// DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
// platforms these days. Just in case some system exists where DBL_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert
(
DBL_DIG
<
20
,
"DBL_DIG is too big."
);
if
(
value
==
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"inf"
);
return
buffer
;
}
else
if
(
value
==
-
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"-inf"
);
return
buffer
;
}
else
if
(
IsNaN
(
value
))
{
strcpy
(
buffer
,
"nan"
);
return
buffer
;
}
int
snprintf_result
=
snprintf
(
buffer
,
kDoubleToBufferSize
,
"%.*g"
,
DBL_DIG
,
value
);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kDoubleToBufferSize
);
// We need to make parsed_value volatile in order to force the compiler to
// write it out to the stack. Otherwise, it may keep the value in a
// register, and if it does that, it may keep it as a long double instead
// of a double. This long double may have extra bits that make it compare
// unequal to "value" even though it would be exactly equal if it were
// truncated to a double.
volatile
double
parsed_value
=
strtod
(
buffer
,
NULL
);
if
(
parsed_value
!=
value
)
{
int
snprintf_result
=
snprintf
(
buffer
,
kDoubleToBufferSize
,
"%.*g"
,
DBL_DIG
+
2
,
value
);
// Should never overflow; see above.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kDoubleToBufferSize
);
}
DelocalizeRadix
(
buffer
);
RemovePlus
(
buffer
);
return
buffer
;
}
bool
safe_strtof
(
const
char
*
str
,
float
*
value
)
{
char
*
endptr
;
errno
=
0
;
// errno only gets set on errors
#if defined(_WIN32) || defined (__hpux) // has no strtof()
*
value
=
strtod
(
str
,
&
endptr
);
#else
*
value
=
strtof
(
str
,
&
endptr
);
#endif
return
*
str
!=
0
&&
*
endptr
==
0
&&
errno
==
0
;
}
char
*
FloatToBuffer
(
float
value
,
char
*
buffer
)
{
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert
(
FLT_DIG
<
10
,
"FLT_DIG is too big"
);
if
(
value
==
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"inf"
);
return
buffer
;
}
else
if
(
value
==
-
std
::
numeric_limits
<
double
>::
infinity
())
{
strcpy
(
buffer
,
"-inf"
);
return
buffer
;
}
else
if
(
IsNaN
(
value
))
{
strcpy
(
buffer
,
"nan"
);
return
buffer
;
}
int
snprintf_result
=
snprintf
(
buffer
,
kFloatToBufferSize
,
"%.*g"
,
FLT_DIG
,
value
);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kFloatToBufferSize
);
float
parsed_value
;
if
(
!
safe_strtof
(
buffer
,
&
parsed_value
)
||
parsed_value
!=
value
)
{
int
snprintf_result
=
snprintf
(
buffer
,
kFloatToBufferSize
,
"%.*g"
,
FLT_DIG
+
2
,
value
);
// Should never overflow; see above.
DCHECK
(
snprintf_result
>
0
&&
snprintf_result
<
kFloatToBufferSize
);
}
DelocalizeRadix
(
buffer
);
RemovePlus
(
buffer
);
return
buffer
;
}
}
// namespace
CappedArray
<
char
,
kFloatToBufferSize
>
Stringifier
::
operator
*
(
float
f
)
const
{
CappedArray
<
char
,
kFloatToBufferSize
>
result
;
result
.
setSize
(
strlen
(
FloatToBuffer
(
f
,
result
.
begin
())));
return
result
;
}
CappedArray
<
char
,
kDoubleToBufferSize
>
Stringifier
::
operator
*
(
double
f
)
const
{
CappedArray
<
char
,
kDoubleToBufferSize
>
result
;
result
.
setSize
(
strlen
(
DoubleToBuffer
(
f
,
result
.
begin
())));
return
result
;
}
}
// namespace kj
c++/src/kj/util.h
deleted
100644 → 0
View file @
47194adc
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef KJ_UTIL_H_
#define KJ_UTIL_H_
#include <initializer_list>
#include <utility>
#include <type_traits>
#include "array.h"
#include "string.h"
#include <string.h>
namespace
kj
{
// =======================================================================================
// String stuff
inline
size_t
sum
(
std
::
initializer_list
<
size_t
>
nums
)
{
size_t
result
=
0
;
for
(
auto
num
:
nums
)
{
result
+=
num
;
}
return
result
;
}
inline
char
*
fill
(
char
*
ptr
)
{
return
ptr
;
}
template
<
typename
First
,
typename
...
Rest
>
char
*
fill
(
char
*
__restrict__
target
,
const
First
&
first
,
Rest
&&
...
rest
)
{
auto
i
=
first
.
begin
();
auto
end
=
first
.
end
();
while
(
i
!=
end
)
{
*
target
++
=
*
i
++
;
}
return
fill
(
target
,
std
::
forward
<
Rest
>
(
rest
)...);
}
template
<
typename
...
Params
>
String
concat
(
Params
&&
...
params
)
{
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `Element`.
String
result
=
heapString
(
sum
({
params
.
size
()...}));
fill
(
result
.
begin
(),
std
::
forward
<
Params
>
(
params
)...);
return
result
;
}
inline
String
concat
(
String
&&
arr
)
{
return
std
::
move
(
arr
);
}
struct
Stringifier
{
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline
ArrayPtr
<
const
char
>
operator
*
(
ArrayPtr
<
const
char
>
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
Array
<
const
char
>&
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
Array
<
char
>&
s
)
const
{
return
s
;
}
template
<
size_t
n
>
inline
ArrayPtr
<
const
char
>
operator
*
(
const
CappedArray
<
char
,
n
>&
s
)
const
{
return
s
;
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
char
*
s
)
const
{
return
arrayPtr
(
s
,
strlen
(
s
));
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
String
&
s
)
const
{
return
s
.
asArray
();
}
inline
ArrayPtr
<
const
char
>
operator
*
(
const
StringPtr
&
s
)
const
{
return
s
.
asArray
();
}
inline
FixedArray
<
char
,
1
>
operator
*
(
char
c
)
const
{
FixedArray
<
char
,
1
>
result
;
result
[
0
]
=
c
;
return
result
;
}
CappedArray
<
char
,
sizeof
(
short
)
*
4
>
operator
*
(
short
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
short
)
*
4
>
operator
*
(
unsigned
short
i
)
const
;
CappedArray
<
char
,
sizeof
(
int
)
*
4
>
operator
*
(
int
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
int
)
*
4
>
operator
*
(
unsigned
int
i
)
const
;
CappedArray
<
char
,
sizeof
(
long
)
*
4
>
operator
*
(
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
long
)
*
4
>
operator
*
(
unsigned
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
long
long
)
*
4
>
operator
*
(
long
long
i
)
const
;
CappedArray
<
char
,
sizeof
(
unsigned
long
long
)
*
4
>
operator
*
(
unsigned
long
long
i
)
const
;
CappedArray
<
char
,
24
>
operator
*
(
float
f
)
const
;
CappedArray
<
char
,
32
>
operator
*
(
double
f
)
const
;
CappedArray
<
char
,
sizeof
(
const
void
*
)
*
4
>
operator
*
(
const
void
*
s
)
const
;
template
<
typename
T
>
Array
<
char
>
operator
*
(
ArrayPtr
<
T
>
arr
)
const
;
template
<
typename
T
>
Array
<
char
>
operator
*
(
const
Array
<
T
>&
arr
)
const
;
};
static
constexpr
Stringifier
STR
=
Stringifier
();
CappedArray
<
char
,
sizeof
(
unsigned
short
)
*
4
>
hex
(
unsigned
short
i
);
CappedArray
<
char
,
sizeof
(
unsigned
int
)
*
4
>
hex
(
unsigned
int
i
);
CappedArray
<
char
,
sizeof
(
unsigned
long
)
*
4
>
hex
(
unsigned
long
i
);
CappedArray
<
char
,
sizeof
(
unsigned
long
long
)
*
4
>
hex
(
unsigned
long
long
i
);
template
<
typename
...
Params
>
String
str
(
Params
&&
...
params
)
{
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return
concat
(
STR
*
std
::
forward
<
Params
>
(
params
)...);
}
template
<
typename
T
>
String
strArray
(
T
&&
arr
,
const
char
*
delim
)
{
size_t
delimLen
=
strlen
(
delim
);
KJ_STACK_ARRAY
(
decltype
(
STR
*
arr
[
0
]),
pieces
,
arr
.
size
(),
8
,
32
);
size_t
size
=
0
;
for
(
size_t
i
=
0
;
i
<
arr
.
size
();
i
++
)
{
if
(
i
>
0
)
size
+=
delimLen
;
pieces
[
i
]
=
STR
*
arr
[
i
];
size
+=
pieces
[
i
].
size
();
}
String
result
=
heapString
(
size
);
char
*
pos
=
result
.
begin
();
for
(
size_t
i
=
0
;
i
<
arr
.
size
();
i
++
)
{
if
(
i
>
0
)
{
memcpy
(
pos
,
delim
,
delimLen
);
pos
+=
delimLen
;
}
pos
=
fill
(
pos
,
pieces
[
i
]);
}
return
result
;
}
template
<
typename
T
>
inline
Array
<
char
>
Stringifier
::
operator
*
(
ArrayPtr
<
T
>
arr
)
const
{
return
strArray
(
arr
,
", "
);
}
template
<
typename
T
>
inline
Array
<
char
>
Stringifier
::
operator
*
(
const
Array
<
T
>&
arr
)
const
{
return
strArray
(
arr
,
", "
);
}
template
<
typename
T
,
typename
Func
>
auto
mapArray
(
T
&&
arr
,
Func
&&
func
)
->
Array
<
decltype
(
func
(
arr
[
0
]))
>
{
// TODO(cleanup): Use ArrayBuilder.
Array
<
decltype
(
func
(
arr
[
0
]))
>
result
=
heapArray
<
decltype
(
func
(
arr
[
0
]))
>
(
arr
.
size
());
size_t
pos
=
0
;
for
(
auto
&
element
:
arr
)
{
result
[
pos
++
]
=
func
(
element
);
}
return
result
;
}
}
// namespace kj
#endif // KJ_UTIL_H_
compiler/src/Compiler.hs
View file @
36c3f8e9
...
...
@@ -855,6 +855,9 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
unions
=
[
d
|
DescUnion
d
<-
members
]
(
finalDataSize
,
finalPointerCount
)
<-
recover
(
dataSize
,
pointerCount
)
$
enforceFixed
isFixed
(
dataSize
,
pointerCount
)
let
memberByNumber
d
@
(
DescField
f
)
=
Just
(
fieldNumber
f
,
d
)
memberByNumber
d
@
(
DescUnion
u
)
=
Just
(
unionNumber
u
,
d
)
memberByNumber
_
=
Nothing
return
(
let
in
DescStruct
StructDesc
{
structName
=
name
...
...
@@ -867,6 +870,7 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
,
structUnions
=
unions
,
structAnnotations
=
compiledAnnotations
,
structMemberMap
=
memberMap
,
structMembersByNumber
=
Map
.
fromList
$
mapMaybe
memberByNumber
members
,
structMembers
=
members
,
structFieldPackingMap
=
fieldPackingMap
})))
...
...
compiler/src/CxxGenerator.hs
View file @
36c3f8e9
...
...
@@ -441,6 +441,8 @@ outerFileContext schemaNodes = fileContext where
unionContext
parent
desc
=
mkStrContext
context
where
titleCase
=
toTitleCase
$
unionName
desc
unionIndex
=
Map
.
findIndex
(
unionNumber
desc
)
$
structMembersByNumber
$
unionParent
desc
context
"typeStruct"
=
MuBool
False
context
"typeUnion"
=
MuBool
True
context
"typeName"
=
MuVariable
titleCase
...
...
@@ -454,6 +456,7 @@ outerFileContext schemaNodes = fileContext where
context
"unionTitleCase"
=
MuVariable
titleCase
context
"unionTagOffset"
=
MuVariable
$
unionTagOffset
desc
context
"unionFields"
=
MuList
$
map
(
fieldContext
context
)
$
unionFields
desc
context
"unionIndex"
=
MuVariable
unionIndex
context
s
=
parent
s
childContext
parent
name
=
mkStrContext
context
where
...
...
compiler/src/Semantics.hs
View file @
36c3f8e9
...
...
@@ -444,6 +444,7 @@ data StructDesc = StructDesc
,
structUnions
::
[
UnionDesc
]
,
structAnnotations
::
AnnotationMap
,
structMemberMap
::
MemberMap
,
structMembersByNumber
::
Map
.
Map
Integer
Desc
-- top-level members only
,
structMembers
::
[
Desc
]
-- Don't use this directly, use the members of FieldDesc and UnionDesc.
...
...
compiler/src/c++-header.mustache
View file @
36c3f8e9
...
...
@@ -119,6 +119,11 @@ CAPNPROTO_DECLARE_STRUCT(
CAPNPROTO_DECLARE_ENUM(
::
{{#
fileNamespaces
}}{{
namespaceName
}}
::
{{/
fileNamespaces
}}{{
typeFullName
}}
::
{{
enumName
}}
,
{{
enumId
}}
);
{{/
structNestedEnums
}}
{{#
structUnions
}}
CAPNPROTO_DECLARE_UNION(
::
{{#
fileNamespaces
}}{{
namespaceName
}}
::
{{/
fileNamespaces
}}{{
structFullName
}}
::
{{
unionTitleCase
}}
,
::
{{#
fileNamespaces
}}{{
namespaceName
}}
::
{{/
fileNamespaces
}}{{
structFullName
}}
,
{{
unionIndex
}}
);
{{/
structUnions
}}
{{/
typeStruct
}}
{{/
typeStructOrUnion
}}
{{/
fileTypes
}}
...
...
@@ -145,9 +150,6 @@ public:
inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {}
{{#
typeStruct
}}
inline ::kj::String debugString() {
return ::capnproto::internal::debugString
<
{{
typeName
}}
>
(_reader);
}
inline size_t totalSizeInWords() {
return _reader.totalSize() / ::capnproto::WORDS;
}
...
...
@@ -186,7 +188,18 @@ private:
template
<typename
T
,
::capnproto::Kind
k
>
friend struct ::capnproto::internal::PointerHelpers;
friend class ::capnproto::MessageBuilder;
friend ::kj::String KJ_STRINGIFY(
{{
typeFullName
}}
::Reader reader);
};
inline ::kj::String KJ_STRINGIFY(
{{
typeFullName
}}
::Reader reader) {
{{#
typeStruct
}}
return ::capnproto::internal::structString
<
{{
typeFullName
}}
>
(reader._reader);
{{/
typeStruct
}}
{{#
typeUnion
}}
return ::capnproto::internal::unionString
<
{{
typeFullName
}}
>
(reader._reader);
{{/
typeUnion
}}
}
{{! ------------------------------------------------------------------------------------------- }}
class
{{
typeFullName
}}
::Builder {
...
...
@@ -199,7 +212,6 @@ public:
inline Reader asReader() { return *this; }
{{#
typeStruct
}}
inline ::kj::String debugString() { return asReader().debugString(); }
inline size_t totalSizeInWords() { return asReader().totalSizeInWords(); }
{{#
structUnions
}}
...
...
@@ -250,7 +262,17 @@ private:
::capnproto::internal::StructBuilder _builder;
template
<typename
T
,
::capnproto::Kind
k
>
friend struct ::capnproto::ToDynamic_;
friend ::kj::String KJ_STRINGIFY(
{{
typeFullName
}}
::Builder builder);
};
inline ::kj::String KJ_STRINGIFY(
{{
typeFullName
}}
::Builder builder) {
{{#
typeStruct
}}
return ::capnproto::internal::structString
<
{{
typeFullName
}}
>
(builder._builder.asReader());
{{/
typeStruct
}}
{{#
typeUnion
}}
return ::capnproto::internal::unionString
<
{{
typeFullName
}}
>
(builder._builder.asReader());
{{/
typeUnion
}}
}
{{/
typeStructOrUnion
}}
{{/
fileTypes
}}
{{! =========================================================================================== }}
...
...
compiler/src/c++-source.mustache
View file @
36c3f8e9
...
...
@@ -85,6 +85,10 @@ CAPNPROTO_DEFINE_STRUCT(
CAPNPROTO_DEFINE_ENUM(
::
{{#
fileNamespaces
}}{{
namespaceName
}}
::
{{/
fileNamespaces
}}{{
typeFullName
}}
::
{{
enumName
}}
);
{{/
structNestedEnums
}}
{{#
structUnions
}}
CAPNPROTO_DEFINE_UNION(
::
{{#
fileNamespaces
}}{{
namespaceName
}}
::
{{/
fileNamespaces
}}{{
structFullName
}}
::
{{
unionTitleCase
}}
);
{{/
structUnions
}}
{{/
typeStruct
}}
{{/
typeStructOrUnion
}}
{{/
fileTypes
}}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment