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
1859b718
Commit
1859b718
authored
9 years ago
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fully implement Orphan::truncate() and allow it to be used even to extend lists.
parent
959ad296
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
586 additions
and
25 deletions
+586
-25
arena.h
c++/src/capnp/arena.h
+15
-0
layout.c++
c++/src/capnp/layout.c++
+113
-15
layout.h
c++/src/capnp/layout.h
+1
-0
list.h
c++/src/capnp/list.h
+2
-2
orphan-test.c++
c++/src/capnp/orphan-test.c++
+437
-1
orphan.h
c++/src/capnp/orphan.h
+17
-7
exception.c++
c++/src/kj/exception.c++
+1
-0
No files found.
c++/src/capnp/arena.h
View file @
1859b718
...
...
@@ -177,6 +177,11 @@ public:
// If `from` points just past the current end of the segment, then move the end back to `to`.
// Otherwise, do nothing.
inline
bool
tryExtend
(
word
*
from
,
word
*
to
);
// If `from` points just past the current end of the segment, and `to` is within the segment
// boundaries, then move the end up to `to` and return true. Otherwise, do nothing and return
// false.
private
:
word
*
pos
;
// Pointer to a pointer to the current end point of the segment, i.e. the location where the
...
...
@@ -445,6 +450,16 @@ inline void SegmentBuilder::tryTruncate(word* from, word* to) {
if
(
pos
==
from
)
pos
=
to
;
}
inline
bool
SegmentBuilder
::
tryExtend
(
word
*
from
,
word
*
to
)
{
// Careful about overflow.
if
(
pos
==
from
&&
to
<=
ptr
.
end
()
&&
to
>=
from
)
{
pos
=
to
;
return
true
;
}
else
{
return
false
;
}
}
}
// namespace _ (private)
}
// namespace capnp
...
...
This diff is collapsed.
Click to expand it.
c++/src/capnp/layout.c++
View file @
1859b718
...
...
@@ -2294,6 +2294,7 @@ void PointerBuilder::transferFrom(PointerBuilder other) {
memset
(
pointer
,
0
,
sizeof
(
*
pointer
));
}
WireHelpers
::
transferPointer
(
segment
,
pointer
,
other
.
segment
,
other
.
pointer
);
memset
(
other
.
pointer
,
0
,
sizeof
(
*
other
.
pointer
));
}
void
PointerBuilder
::
copyFrom
(
PointerReader
other
)
{
...
...
@@ -2613,10 +2614,10 @@ kj::ArrayPtr<const byte> ListReader::asRawBytes() {
return
kj
::
ArrayPtr
<
const
byte
>
();
}
return
kj
::
ArrayPtr
<
const
byte
>
(
reinterpret_cast
<
const
byte
*>
(
ptr
),
structDataSize
*
elementCount
/
ELEMENTS
);
return
kj
::
ArrayPtr
<
const
byte
>
(
reinterpret_cast
<
const
byte
*>
(
ptr
),
WireHelpers
::
roundBitsUpToBytes
(
elementCount
*
(
structDataSize
/
ELEMENTS
))
/
BYTES
);
}
StructReader
ListReader
::
getStructElement
(
ElementCount
index
)
const
{
KJ_REQUIRE
(
nestingLimit
>
0
,
"Message is too deeply-nested or contains cycles. See capnp::ReaderOptions."
)
{
...
...
@@ -2851,26 +2852,123 @@ void OrphanBuilder::truncate(ElementCount size, bool isText) {
return
;
}
// TODO(someday): Implement truncation of all sizes.
KJ_ASSERT
(
ref
->
listRef
.
elementSize
()
==
ElementSize
::
BYTE
,
"Not implemented: truncate non-blob."
);
ElementSize
elementSize
=
ref
->
listRef
.
elementSize
();
auto
oldSize
=
ref
->
listRef
.
elementCount
();
KJ_REQUIRE
(
size
<=
oldSize
,
"Truncate size must be smaller than existing size."
)
{
if
(
elementSize
==
ElementSize
::
INLINE_COMPOSITE
)
{
WordCount
oldWordCount
=
ref
->
listRef
.
inlineCompositeWordCount
();
WirePointer
*
tag
=
reinterpret_cast
<
WirePointer
*>
(
target
);
++
target
;
KJ_REQUIRE
(
tag
->
kind
()
==
WirePointer
::
STRUCT
,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported."
)
{
return
;
}
StructSize
structSize
(
tag
->
structRef
.
dataSize
.
get
(),
tag
->
structRef
.
ptrCount
.
get
());
WordCount
elementWordCount
=
structSize
.
total
();
ElementCount
oldSize
=
tag
->
inlineCompositeListElementCount
();
word
*
newEndWord
=
target
+
size
*
(
elementWordCount
/
ELEMENTS
);
word
*
oldEndWord
=
target
+
oldWordCount
;
if
(
size
<=
oldSize
)
{
// Zero the trailing elements.
for
(
uint
i
=
size
/
ELEMENTS
;
i
<
oldSize
/
ELEMENTS
;
i
++
)
{
WireHelpers
::
zeroObject
(
segment
,
tag
,
target
+
i
*
elementWordCount
);
}
ref
->
listRef
.
setInlineComposite
(
size
*
(
elementWordCount
/
ELEMENTS
));
tag
->
setKindAndInlineCompositeListElementCount
(
WirePointer
::
STRUCT
,
size
);
segment
->
tryTruncate
(
oldEndWord
,
newEndWord
);
}
else
if
(
newEndWord
<=
oldEndWord
)
{
// Apparently the old list was over-allecated? The word count is more than needed to store
// the elements. This is "valid" but shouldn't happen in practice unless someone is toying
// with us.
word
*
expectedEnd
=
target
+
oldSize
*
(
elementWordCount
/
ELEMENTS
);
KJ_ASSERT
(
newEndWord
>=
expectedEnd
);
memset
(
expectedEnd
,
0
,
(
newEndWord
-
expectedEnd
)
*
sizeof
(
word
));
tag
->
setKindAndInlineCompositeListElementCount
(
WirePointer
::
STRUCT
,
size
);
}
else
{
if
(
segment
->
tryExtend
(
oldEndWord
,
newEndWord
))
{
// Done in-place. Nothing else to do now; the new memory is already zero'd.
ref
->
listRef
.
setInlineComposite
(
size
*
(
elementWordCount
/
ELEMENTS
));
tag
->
setKindAndInlineCompositeListElementCount
(
WirePointer
::
STRUCT
,
size
);
}
else
{
// Need to re-allocate and transfer.
StructSize
structSize
(
tag
->
structRef
.
dataSize
.
get
(),
tag
->
structRef
.
ptrCount
.
get
());
OrphanBuilder
replacement
=
initStructList
(
segment
->
getArena
(),
size
,
structSize
);
ref
->
listRef
.
set
(
ref
->
listRef
.
elementSize
(),
size
);
ListBuilder
newList
=
replacement
.
asStructList
(
structSize
);
word
*
element
=
target
;
for
(
uint
i
=
0
;
i
<
oldSize
/
ELEMENTS
;
i
++
)
{
newList
.
getStructElement
(
i
*
ELEMENTS
).
transferContentFrom
(
StructBuilder
(
segment
,
element
,
reinterpret_cast
<
WirePointer
*>
(
element
+
structSize
.
data
),
structSize
.
data
*
BITS_PER_WORD
,
structSize
.
pointers
));
element
+=
elementWordCount
;
}
byte
*
begin
=
reinterpret_cast
<
byte
*>
(
target
);
byte
*
truncPoint
=
begin
+
size
*
(
1
*
BYTES
/
ELEMENTS
);
byte
*
end
=
begin
+
oldSize
*
(
1
*
BYTES
/
ELEMENTS
);
memset
(
truncPoint
-
isText
,
0
,
end
-
truncPoint
+
isText
);
*
this
=
kj
::
mv
(
replacement
);
}
}
}
else
if
(
elementSize
==
ElementSize
::
POINTER
)
{
auto
oldSize
=
ref
->
listRef
.
elementCount
();
word
*
newEndWord
=
target
+
size
*
(
POINTER_SIZE_IN_WORDS
/
ELEMENTS
);
word
*
oldEndWord
=
target
+
oldSize
*
(
POINTER_SIZE_IN_WORDS
/
ELEMENTS
);
word
*
truncWord
=
target
+
WireHelpers
::
roundBytesUpToWords
(
size
*
(
1
*
BYTES
/
ELEMENTS
));
word
*
endWord
=
target
+
WireHelpers
::
roundBytesUpToWords
(
oldSize
*
(
1
*
BYTES
/
ELEMENTS
));
if
(
size
<=
oldSize
)
{
// Zero the trailing elements.
for
(
WirePointer
*
element
=
reinterpret_cast
<
WirePointer
*>
(
newEndWord
);
element
<
reinterpret_cast
<
WirePointer
*>
(
oldEndWord
);
++
element
)
{
WireHelpers
::
zeroPointerAndFars
(
segment
,
element
);
}
ref
->
listRef
.
set
(
ElementSize
::
POINTER
,
size
);
segment
->
tryTruncate
(
oldEndWord
,
newEndWord
);
}
else
{
if
(
segment
->
tryExtend
(
oldEndWord
,
newEndWord
))
{
// Done in-place. Nothing else to do now; the new memory is already zero'd.
ref
->
listRef
.
set
(
ElementSize
::
POINTER
,
size
);
}
else
{
// Need to re-allocate and transfer.
OrphanBuilder
replacement
=
initList
(
segment
->
getArena
(),
size
,
ElementSize
::
POINTER
);
ListBuilder
newList
=
replacement
.
asList
(
ElementSize
::
POINTER
);
WirePointer
*
oldPointers
=
reinterpret_cast
<
WirePointer
*>
(
target
);
for
(
uint
i
=
0
;
i
<
oldSize
/
ELEMENTS
;
i
++
)
{
newList
.
getPointerElement
(
i
*
ELEMENTS
).
transferFrom
(
PointerBuilder
(
segment
,
oldPointers
+
i
));
}
*
this
=
kj
::
mv
(
replacement
);
}
}
}
else
{
auto
oldSize
=
ref
->
listRef
.
elementCount
();
auto
step
=
dataBitsPerElement
(
elementSize
);
word
*
newEndWord
=
target
+
WireHelpers
::
roundBitsUpToWords
(
size
*
step
);
word
*
oldEndWord
=
target
+
WireHelpers
::
roundBitsUpToWords
(
oldSize
*
step
);
segment
->
tryTruncate
(
endWord
,
truncWord
);
if
(
size
<=
oldSize
)
{
// When truncating text, we want to set the null terminator as well, so we'll do our zeroing
// at the byte level.
byte
*
begin
=
reinterpret_cast
<
byte
*>
(
target
);
byte
*
newEndByte
=
begin
+
WireHelpers
::
roundBitsUpToBytes
(
size
*
step
)
-
isText
;
byte
*
oldEndByte
=
reinterpret_cast
<
byte
*>
(
oldEndWord
);
memset
(
newEndByte
,
0
,
oldEndByte
-
newEndByte
);
ref
->
listRef
.
set
(
elementSize
,
size
);
segment
->
tryTruncate
(
oldEndWord
,
newEndWord
);
}
else
{
// We're trying to extend, not truncate.
if
(
segment
->
tryExtend
(
oldEndWord
,
newEndWord
))
{
// Done in-place. Nothing else to do now; the memory is already zero'd.
ref
->
listRef
.
set
(
elementSize
,
size
);
}
else
{
// Need to re-allocate and transfer.
OrphanBuilder
replacement
=
initList
(
segment
->
getArena
(),
size
,
elementSize
);
ListBuilder
newList
=
replacement
.
asList
(
elementSize
);
auto
words
=
WireHelpers
::
roundBitsUpToWords
(
dataBitsPerElement
(
elementSize
)
*
oldSize
);
memcpy
(
newList
.
ptr
,
target
,
words
*
BYTES_PER_WORD
/
BYTES
);
*
this
=
kj
::
mv
(
replacement
);
}
}
}
}
void
OrphanBuilder
::
euthanize
()
{
...
...
This diff is collapsed.
Click to expand it.
c++/src/capnp/layout.h
View file @
1859b718
...
...
@@ -334,6 +334,7 @@ private:
friend
class
StructBuilder
;
friend
class
ListBuilder
;
friend
class
OrphanBuilder
;
};
class
PointerReader
{
...
...
This diff is collapsed.
Click to expand it.
c++/src/capnp/list.h
View file @
1859b718
...
...
@@ -386,7 +386,7 @@ struct List<List<T>, Kind::LIST> {
}
inline
void
adopt
(
uint
index
,
Orphan
<
T
>&&
value
)
{
KJ_IREQUIRE
(
index
<
size
());
builder
.
getPointerElement
(
index
*
ELEMENTS
).
adopt
(
kj
::
mv
(
value
));
builder
.
getPointerElement
(
index
*
ELEMENTS
).
adopt
(
kj
::
mv
(
value
.
builder
));
}
inline
Orphan
<
T
>
disown
(
uint
index
)
{
KJ_IREQUIRE
(
index
<
size
());
...
...
@@ -484,7 +484,7 @@ struct List<T, Kind::BLOB> {
}
inline
void
adopt
(
uint
index
,
Orphan
<
T
>&&
value
)
{
KJ_IREQUIRE
(
index
<
size
());
builder
.
getPointerElement
(
index
*
ELEMENTS
).
adopt
(
kj
::
mv
(
value
));
builder
.
getPointerElement
(
index
*
ELEMENTS
).
adopt
(
kj
::
mv
(
value
.
builder
));
}
inline
Orphan
<
T
>
disown
(
uint
index
)
{
KJ_IREQUIRE
(
index
<
size
());
...
...
This diff is collapsed.
Click to expand it.
c++/src/capnp/orphan-test.c++
View file @
1859b718
...
...
@@ -992,7 +992,7 @@ TEST(Orphans, ReferenceExternalData_NoZeroImmediateAbandon) {
}
}
TEST
(
Orphans
,
Truncate
)
{
TEST
(
Orphans
,
Truncate
Data
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
17
);
auto
builder
=
orphan
.
get
();
...
...
@@ -1013,6 +1013,54 @@ TEST(Orphans, Truncate) {
EXPECT_EQ
(
0
,
builder
[
16
]);
}
TEST
(
Orphans
,
ExtendData
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
17
);
auto
builder
=
orphan
.
get
();
memset
(
builder
.
begin
(),
123
,
builder
.
size
());
EXPECT_EQ
(
4
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
27
);
EXPECT_EQ
(
5
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
27
,
reader
.
size
());
EXPECT_EQ
(
builder
.
begin
(),
reader
.
begin
());
for
(
uint
i
=
0
;
i
<
17
;
i
++
)
{
EXPECT_EQ
(
123
,
reader
[
i
]);
}
for
(
uint
i
=
17
;
i
<
27
;
i
++
)
{
EXPECT_EQ
(
0
,
reader
[
i
]);
}
}
TEST
(
Orphans
,
ExtendDataCopy
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
17
);
auto
builder
=
orphan
.
get
();
memset
(
builder
.
begin
(),
123
,
builder
.
size
());
auto
orphan2
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
1
);
orphan2
.
get
()[
0
]
=
32
;
orphan
.
truncate
(
27
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
27
,
reader
.
size
());
EXPECT_NE
(
builder
.
begin
(),
reader
.
begin
());
for
(
uint
i
=
0
;
i
<
17
;
i
++
)
{
EXPECT_EQ
(
123
,
reader
[
i
]);
EXPECT_EQ
(
0
,
builder
[
i
]);
}
for
(
uint
i
=
17
;
i
<
27
;
i
++
)
{
EXPECT_EQ
(
0
,
reader
[
i
]);
}
EXPECT_EQ
(
32
,
orphan2
.
getReader
()[
0
]);
}
TEST
(
Orphans
,
TruncateText
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Text
>
(
17
);
...
...
@@ -1034,6 +1082,394 @@ TEST(Orphans, TruncateText) {
EXPECT_EQ
(
'\0'
,
builder
[
16
]);
}
TEST
(
Orphans
,
ExtendText
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Text
>
(
17
);
auto
builder
=
orphan
.
get
();
memset
(
builder
.
begin
(),
'a'
,
builder
.
size
());
EXPECT_EQ
(
4
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
27
);
EXPECT_EQ
(
5
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
27
,
reader
.
size
());
EXPECT_EQ
(
builder
.
begin
(),
reader
.
begin
());
for
(
uint
i
=
0
;
i
<
17
;
i
++
)
{
EXPECT_EQ
(
'a'
,
reader
[
i
]);
}
for
(
uint
i
=
17
;
i
<
27
;
i
++
)
{
EXPECT_EQ
(
'\0'
,
reader
[
i
]);
}
}
TEST
(
Orphans
,
ExtendTextCopy
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
Text
>
(
17
);
auto
builder
=
orphan
.
get
();
memset
(
builder
.
begin
(),
'a'
,
builder
.
size
());
auto
orphan2
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
1
);
orphan2
.
get
()[
0
]
=
32
;
orphan
.
truncate
(
27
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
27
,
reader
.
size
());
EXPECT_NE
(
builder
.
begin
(),
reader
.
begin
());
for
(
uint
i
=
0
;
i
<
17
;
i
++
)
{
EXPECT_EQ
(
'a'
,
reader
[
i
]);
EXPECT_EQ
(
'\0'
,
builder
[
i
]);
}
for
(
uint
i
=
17
;
i
<
27
;
i
++
)
{
EXPECT_EQ
(
'\0'
,
reader
[
i
]);
}
EXPECT_EQ
(
32
,
orphan2
.
getReader
()[
0
]);
}
TEST
(
Orphans
,
TruncatePrimitiveList
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
uint32_t
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
set
(
i
,
123456789
+
i
);
}
EXPECT_EQ
(
5
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
3
);
EXPECT_EQ
(
3
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
3
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
3
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
builder
[
i
]);
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
]);
}
for
(
uint
i
=
3
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
0
,
builder
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
321
);
EXPECT_EQ
(
321
,
reader
[
0
]);
}
TEST
(
Orphans
,
ExtendPrimitiveList
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
uint32_t
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
set
(
i
,
123456789
+
i
);
}
EXPECT_EQ
(
5
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
11
);
EXPECT_EQ
(
7
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
]);
EXPECT_EQ
(
123456789
+
i
,
builder
[
i
]);
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
EXPECT_EQ
(
0
,
reader
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
321
);
EXPECT_EQ
(
321
,
reader
[
0
]);
}
TEST
(
Orphans
,
ExtendPrimitiveListCopy
)
{
MallocMessageBuilder
message
;
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
uint32_t
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
set
(
i
,
123456789
+
i
);
}
auto
orphan2
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
1
);
orphan2
.
get
()[
0
]
=
32
;
orphan
.
truncate
(
11
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
]);
EXPECT_EQ
(
0
,
builder
[
i
]);
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
EXPECT_EQ
(
0
,
reader
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
321
);
EXPECT_EQ
(
123456789
,
reader
[
0
]);
EXPECT_EQ
(
32
,
orphan2
.
getReader
()[
0
]);
}
TEST
(
Orphans
,
TruncatePointerList
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
Text
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
pointers
.
add
(
message
.
getOrphanage
().
newOrphanCopy
(
Text
::
Reader
(
kj
::
str
(
"foo"
,
i
))));
}
size_t
sizeBeforeList
=
message
.
getSegmentsForOutput
()[
0
].
size
();
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
Text
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adopt
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
EXPECT_EQ
(
sizeBeforeList
+
7
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
3
);
EXPECT_EQ
(
sizeBeforeList
+
3
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
3
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
3
;
i
++
)
{
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
builder
[
i
]);
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
]);
}
for
(
uint
i
=
3
;
i
<
7
;
i
++
)
{
EXPECT_TRUE
(
builder
[
i
]
==
nullptr
);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
"bar"
);
EXPECT_EQ
(
"bar"
,
reader
[
0
]);
}
TEST
(
Orphans
,
ExtendPointerList
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
Text
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
pointers
.
add
(
message
.
getOrphanage
().
newOrphanCopy
(
Text
::
Reader
(
kj
::
str
(
"foo"
,
i
))));
}
size_t
sizeBeforeList
=
message
.
getSegmentsForOutput
()[
0
].
size
();
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
Text
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adopt
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
EXPECT_EQ
(
sizeBeforeList
+
7
,
message
.
getSegmentsForOutput
()[
0
].
size
());
orphan
.
truncate
(
11
);
EXPECT_EQ
(
sizeBeforeList
+
11
,
message
.
getSegmentsForOutput
()[
0
].
size
());
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
]);
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
builder
[
i
]);
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
EXPECT_TRUE
(
reader
[
i
]
==
nullptr
);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
"bar"
);
EXPECT_EQ
(
"bar"
,
reader
[
0
]);
}
TEST
(
Orphans
,
ExtendPointerListCopy
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
Text
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
pointers
.
add
(
message
.
getOrphanage
().
newOrphanCopy
(
Text
::
Reader
(
kj
::
str
(
"foo"
,
i
))));
}
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
Text
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adopt
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
auto
orphan2
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
1
);
orphan2
.
get
()[
0
]
=
32
;
orphan
.
truncate
(
11
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
]);
EXPECT_TRUE
(
builder
[
i
]
==
nullptr
);
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
EXPECT_TRUE
(
reader
[
i
]
==
nullptr
);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
.
set
(
0
,
"bar"
);
EXPECT_EQ
(
"foo0"
,
reader
[
0
]);
EXPECT_EQ
(
32
,
orphan2
.
getReader
()[
0
]);
}
TEST
(
Orphans
,
TruncateStructList
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
TestAllTypes
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
auto
o
=
message
.
getOrphanage
().
newOrphan
<
TestAllTypes
>
();
auto
b
=
o
.
get
();
b
.
setUInt32Field
(
123456789
+
i
);
b
.
setTextField
(
kj
::
str
(
"foo"
,
i
));
b
.
setUInt8List
({
123
,
45
});
pointers
.
add
(
kj
::
mv
(
o
));
}
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
TestAllTypes
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adoptWithCaveats
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
size_t
sizeBeforeTruncate
=
message
.
getSegmentsForOutput
()[
0
].
size
();
orphan
.
truncate
(
3
);
EXPECT_LT
(
message
.
getSegmentsForOutput
()[
0
].
size
(),
sizeBeforeTruncate
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
3
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
3
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
].
getUInt32Field
());
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
].
getTextField
());
checkList
(
reader
[
i
].
getUInt8List
(),
{
123
,
45
});
EXPECT_EQ
(
123456789
+
i
,
builder
[
i
].
getUInt32Field
());
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
builder
[
i
].
getTextField
());
checkList
(
builder
[
i
].
getUInt8List
(),
{
123
,
45
});
}
for
(
uint
i
=
3
;
i
<
7
;
i
++
)
{
checkTestMessageAllZero
(
builder
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
[
0
].
setUInt32Field
(
321
);
EXPECT_EQ
(
321
,
reader
[
0
].
getUInt32Field
());
}
TEST
(
Orphans
,
ExtendStructList
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
TestAllTypes
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
auto
o
=
message
.
getOrphanage
().
newOrphan
<
TestAllTypes
>
();
auto
b
=
o
.
get
();
b
.
setUInt32Field
(
123456789
+
i
);
b
.
setTextField
(
kj
::
str
(
"foo"
,
i
));
b
.
setUInt8List
({
123
,
45
});
pointers
.
add
(
kj
::
mv
(
o
));
}
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
TestAllTypes
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adoptWithCaveats
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
orphan
.
truncate
(
11
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
].
getUInt32Field
());
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
].
getTextField
());
checkList
(
reader
[
i
].
getUInt8List
(),
{
123
,
45
});
EXPECT_EQ
(
123456789
+
i
,
builder
[
i
].
getUInt32Field
());
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
builder
[
i
].
getTextField
());
checkList
(
builder
[
i
].
getUInt8List
(),
{
123
,
45
});
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
checkTestMessageAllZero
(
reader
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
[
0
].
setUInt32Field
(
321
);
EXPECT_EQ
(
321
,
reader
[
0
].
getUInt32Field
());
}
TEST
(
Orphans
,
ExtendStructListCopy
)
{
MallocMessageBuilder
message
;
// Allocate data in advance so that the list itself is at the end of the segment.
kj
::
Vector
<
Orphan
<
TestAllTypes
>>
pointers
(
7
);
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
auto
o
=
message
.
getOrphanage
().
newOrphan
<
TestAllTypes
>
();
auto
b
=
o
.
get
();
b
.
setUInt32Field
(
123456789
+
i
);
b
.
setTextField
(
kj
::
str
(
"foo"
,
i
));
b
.
setUInt8List
({
123
,
45
});
pointers
.
add
(
kj
::
mv
(
o
));
}
auto
orphan
=
message
.
getOrphanage
().
newOrphan
<
List
<
TestAllTypes
>>
(
7
);
auto
builder
=
orphan
.
get
();
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
builder
.
adoptWithCaveats
(
i
,
kj
::
mv
(
pointers
[
i
]));
}
auto
orphan2
=
message
.
getOrphanage
().
newOrphan
<
Data
>
(
1
);
orphan2
.
get
()[
0
]
=
32
;
orphan
.
truncate
(
11
);
auto
reader
=
orphan
.
getReader
();
EXPECT_EQ
(
11
,
reader
.
size
());
for
(
uint
i
=
0
;
i
<
7
;
i
++
)
{
EXPECT_EQ
(
123456789
+
i
,
reader
[
i
].
getUInt32Field
());
EXPECT_EQ
(
kj
::
str
(
"foo"
,
i
),
reader
[
i
].
getTextField
());
checkList
(
reader
[
i
].
getUInt8List
(),
{
123
,
45
});
checkTestMessageAllZero
(
builder
[
i
]);
}
for
(
uint
i
=
7
;
i
<
11
;
i
++
)
{
checkTestMessageAllZero
(
reader
[
i
]);
}
// Can't compare pointers directly, but we can check if builder modifications are visible using
// the reader.
builder
[
0
].
setUInt32Field
(
321
);
EXPECT_EQ
(
123456789
,
reader
[
0
].
getUInt32Field
());
EXPECT_EQ
(
32
,
orphan2
.
getReader
()[
0
]);
}
}
// namespace
}
// namespace _ (private)
}
// namespace capnp
This diff is collapsed.
Click to expand it.
c++/src/capnp/orphan.h
View file @
1859b718
...
...
@@ -67,14 +67,24 @@ public:
inline
bool
operator
!=
(
decltype
(
nullptr
))
const
{
return
builder
!=
nullptr
;
}
inline
void
truncate
(
uint
size
);
// Truncate the object (which must be a list or a blob) down to the given size. The object's
// current size must be larger than this. The object stays in its current position. If the object
// is the last object in its segment (which is always true if the object is the last thing that
// was allocated in the message) then the truncated space can be reclaimed. Otherwise, the space
// is zero'd out but otherwise lost, like an abandoned orphan.
// Resize an object (which must be a list or a blob) to the given size.
//
// Any existing readers or builders pointing at the object are invalidated by this call. You
// must call `get()` or `getReader()` again to get the new, valid pointer.
// If the new size is less than the original, the remaining elements will be discarded. The
// list is never moved in this case. If the list happens to be located at the end of its segment
// (which is always true if the list was the last thing allocated), the removed memory will be
// reclaimed (reducing the messag size), otherwise it is simply zeroed. The reclaiming behavior
// is particularly useful for allocating buffer space when you aren't sure how much space you
// actually need: you can pre-allocate, say, a 4k byte array, read() from a file into it, and
// then truncate it back to the amount of space actually used.
//
// If the new size is greater than the original, the list is extended with default values. If
// the list is the last object in its segment *and* there is enough space left in the segment to
// extend it to cover the new values, then the list is extended in-place. Otherwise, it must be
// moved to a new location, leaving a zero'd hole in the previous space that won't be filled.
// This copy is shallow; sub-objects will simply be reparented, not copied.
//
// Any existing readers or builders pointing at the object are invalidated by this call (even if
// it doesn't move). You must call `get()` or `getReader()` again to get the new, valid pointer.
private
:
_
::
OrphanBuilder
builder
;
...
...
This diff is collapsed.
Click to expand it.
c++/src/kj/exception.c++
View file @
1859b718
...
...
@@ -195,6 +195,7 @@ void printStackTraceOnCrash() {
KJ_SYSCALL
(
sigaction
(
SIGBUS
,
&
action
,
nullptr
));
KJ_SYSCALL
(
sigaction
(
SIGFPE
,
&
action
,
nullptr
));
KJ_SYSCALL
(
sigaction
(
SIGABRT
,
&
action
,
nullptr
));
KJ_SYSCALL
(
sigaction
(
SIGILL
,
&
action
,
nullptr
));
// Dump stack on unimplemented syscalls -- useful in seccomp sandboxes.
KJ_SYSCALL
(
sigaction
(
SIGSYS
,
&
action
,
nullptr
));
...
...
This diff is collapsed.
Click to expand it.
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