Merge "Use -Werror in system/core/libnativebridge"
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 10753ce..8c11289 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -227,6 +227,7 @@
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
+ {"usb", 90},
};
// Converts a string value representing the reason the system booted to an
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index c93e2ab..5825e6c 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -334,6 +334,29 @@
// Template within templates.
ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
+
+ ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
+ ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
+ ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
+ ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
+ ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
+ ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
+ ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
+ // Template within templates.
+ ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
+ ASSERT_EQ("one(two<three<char, four<int>>>)",
+ demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
+}
+
+TEST(DemangleTest, TemplateFunctionWithReturnType) {
+ Demangler demangler;
+
+ ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
+ ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
+ ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
+ ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
+ ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
+ ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
}
TEST(DemangleTest, TemplateArguments) {
@@ -410,6 +433,28 @@
demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
}
+TEST(DemangleTest, TemplateSubstitution) {
+ Demangler demangler;
+
+ ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
+ ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
+ ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
+
+ ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
+ ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
+ ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
+
+ ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+ demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
+ ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+ demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
+
+ ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+ demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
+ ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+ demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
+}
+
TEST(DemangleTest, StringTooLong) {
Demangler demangler;
@@ -434,6 +479,13 @@
ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+
+ ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
+ ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
+ ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
+
+ ASSERT_EQ("one(two<three<four>, false, true>)",
+ demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
}
TEST(DemangleTest, demangle) {
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index f148b21..18a90b4 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -347,6 +347,33 @@
return name + 1;
}
+const char* Demangler::ParseT(const char* name) {
+ if (template_saves_.empty()) {
+ return nullptr;
+ }
+
+ if (*name == '_') {
+ last_save_name_ = false;
+ AppendCurrent(template_saves_[0]);
+ return name + 1;
+ }
+
+ // Need to get the total number.
+ char* end;
+ unsigned long int index = strtoul(name, &end, 10) + 1;
+ if (name == end || *end != '_') {
+ return nullptr;
+ }
+
+ if (index >= template_saves_.size()) {
+ return nullptr;
+ }
+
+ last_save_name_ = false;
+ AppendCurrent(template_saves_[index]);
+ return end + 1;
+}
+
const char* Demangler::ParseFunctionName(const char* name) {
if (*name == 'E') {
if (parse_funcs_.empty()) {
@@ -371,9 +398,28 @@
return name + 1;
}
+ if (*name == 'I') {
+ state_stack_.push(cur_state_);
+ cur_state_.Clear();
+
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseFunctionNameTemplate;
+ return name + 1;
+ }
+
return ParseComplexString(name);
}
+const char* Demangler::ParseFunctionNameTemplate(const char* name) {
+ if (*name == 'E' && name[1] == 'E') {
+ // Only consider this a template with saves if it is right before
+ // the end of the name.
+ template_found_ = true;
+ template_saves_ = cur_state_.args;
+ }
+ return ParseTemplateArgumentsComplex(name);
+}
+
const char* Demangler::ParseComplexArgument(const char* name) {
if (*name == 'E') {
if (parse_funcs_.empty()) {
@@ -690,6 +736,7 @@
}
parse_func_ = parse_funcs_.back();
parse_funcs_.pop_back();
+
FinalizeTemplate();
Save(cur_state_.str, false);
return name + 1;
@@ -699,6 +746,7 @@
parse_func_ = &Demangler::ParseTemplateLiteral;
return name + 1;
}
+
return ParseArguments(name);
}
@@ -713,13 +761,33 @@
AppendArgument(cur_state_.str);
cur_state_.str.clear();
return name + 1;
+ } else if (*name == 'L') {
+ // Literal value for a template.
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseTemplateLiteral;
+ return name + 1;
}
+
return ParseArguments(name);
}
+const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
+ if (*name == 'E') {
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+
+ function_name_ += '<' + GetArgumentsString() + '>';
+ template_found_ = true;
+ template_saves_ = cur_state_.args;
+ cur_state_.Clear();
+ return name + 1;
+ }
+ return ParseTemplateArgumentsComplex(name);
+}
+
const char* Demangler::FindFunctionName(const char* name) {
if (*name == 'N') {
- parse_funcs_.push_back(&Demangler::ParseArguments);
+ parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
parse_func_ = &Demangler::ParseFunctionName;
return name + 1;
}
@@ -732,11 +800,35 @@
name = AppendOperatorString(name);
function_name_ = cur_state_.str;
}
- parse_func_ = &Demangler::ParseArguments;
cur_state_.Clear();
+
+ // Check for a template argument, which will still be part of the function
+ // name.
+ if (name != nullptr && *name == 'I') {
+ parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
+ parse_func_ = &Demangler::ParseFunctionTemplateArguments;
+ return name + 1;
+ }
+ parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
return name;
}
+const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
+ // At the top level is the only place where T is allowed.
+ if (*name == 'T') {
+ name++;
+ name = ParseT(name);
+ if (name == nullptr) {
+ return nullptr;
+ }
+ AppendArgument(cur_state_.str);
+ cur_state_.str.clear();
+ return name;
+ }
+
+ return Demangler::ParseArguments(name);
+}
+
std::string Demangler::Parse(const char* name, size_t max_length) {
if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
// Name is not mangled.
@@ -757,6 +849,21 @@
return name;
}
+ std::string return_type;
+ if (template_found_) {
+ // Only a single argument with a template is not allowed.
+ if (cur_state_.args.size() == 1) {
+ return name;
+ }
+
+ // If there are at least two arguments, this template has a return type.
+ if (cur_state_.args.size() > 1) {
+ // The first argument will be the return value.
+ return_type = cur_state_.args[0] + ' ';
+ cur_state_.args.erase(cur_state_.args.begin());
+ }
+ }
+
std::string arg_str;
if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
// If the only argument is void, then don't print any args.
@@ -767,7 +874,7 @@
arg_str = '(' + arg_str + ')';
}
}
- return function_name_ + arg_str + function_suffix_;
+ return return_type + function_name_ + arg_str + function_suffix_;
}
std::string demangle(const char* name) {
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
index f76def6..3b7d44e 100644
--- a/demangle/Demangler.h
+++ b/demangle/Demangler.h
@@ -39,6 +39,7 @@
std::string GetArgumentsString();
void FinalizeTemplate();
const char* ParseS(const char* name);
+ const char* ParseT(const char* name);
const char* AppendOperatorString(const char* name);
void Save(const std::string& str, bool is_name);
@@ -50,17 +51,21 @@
first_save_.clear();
cur_state_.Clear();
saves_.clear();
+ template_saves_.clear();
while (!state_stack_.empty()) {
state_stack_.pop();
}
last_save_name_ = false;
+ template_found_ = false;
}
using parse_func_type = const char* (Demangler::*)(const char*);
parse_func_type parse_func_;
std::vector<parse_func_type> parse_funcs_;
std::vector<std::string> saves_;
+ std::vector<std::string> template_saves_;
bool last_save_name_;
+ bool template_found_;
std::string function_name_;
std::string function_suffix_;
@@ -89,12 +94,15 @@
// Parsing functions.
const char* ParseComplexString(const char* name);
const char* ParseComplexArgument(const char* name);
+ const char* ParseArgumentsAtTopLevel(const char* name);
const char* ParseArguments(const char* name);
const char* ParseTemplateArguments(const char* name);
const char* ParseTemplateArgumentsComplex(const char* name);
const char* ParseTemplateLiteral(const char* name);
const char* ParseFunctionArgument(const char* name);
const char* ParseFunctionName(const char* name);
+ const char* ParseFunctionNameTemplate(const char* name);
+ const char* ParseFunctionTemplateArguments(const char* name);
const char* FindFunctionName(const char* name);
const char* Fail(const char*) { return nullptr; }
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index b8164c5..2292168 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -107,7 +107,6 @@
return false;
}
- AddressType prev_pc = regs->pc();
AddressType prev_cfa = regs->sp();
AddressType cfa;
@@ -233,8 +232,8 @@
*finished = (cur_regs->pc() == 0) ? true : false;
cur_regs->set_sp(cfa);
- // Return false if the unwind is not finished or the cfa and pc didn't change.
- return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc();
+
+ return true;
}
template <typename AddressType>
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2190711..2e46a11 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -87,8 +87,10 @@
bool return_address_attempt = false;
bool adjust_pc = false;
for (; frames_.size() < max_frames_;) {
- MapInfo* map_info = maps_->Find(regs_->pc());
+ uint64_t cur_pc = regs_->pc();
+ uint64_t cur_sp = regs_->sp();
+ MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
Elf* elf;
if (map_info == nullptr) {
@@ -138,6 +140,7 @@
}
}
}
+
if (!stepped) {
if (return_address_attempt) {
// Remove the speculative frame.
@@ -157,6 +160,11 @@
} else {
return_address_attempt = false;
}
+
+ // If the pc and sp didn't change, then consider everything stopped.
+ if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
+ break;
+ }
}
}
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 7e85bbb..5b9f3ee 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -431,22 +431,6 @@
EXPECT_EQ(0x80000000U, regs.pc());
}
-TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
- DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
- dwarf_loc_regs_t loc_regs;
-
- regs.set_pc(0x100);
- regs.set_sp(0x2000);
- regs[5] = 0x100;
- regs[8] = 0x2000;
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
- bool finished;
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
- EXPECT_EQ(0x2000U, regs.sp());
- EXPECT_EQ(0x100U, regs.pc());
-}
-
TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
@@ -872,9 +856,9 @@
Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
- Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
- GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
- GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+ GetCie_fail_should_not_cache, GetCie_32_version_check, GetCie_negative_data_alignment_factor,
+ GetCie_64_no_augment, GetCie_augment, GetCie_version_3, GetCie_version_4,
+ GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
GetCfaLocationInfo_cie_cached, Log);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 8a90bae..869d118 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -127,6 +127,7 @@
void SetUp() override {
ElfInterfaceFake::FakeClear();
regs_.FakeSetMachineType(EM_ARM);
+ regs_.FakeSetReturnAddressValid(false);
}
static MapsFake maps_;
@@ -610,6 +611,71 @@
EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
}
+// Verify that an unwind stops when the sp and pc don't change.
+TEST_F(UnwinderTest, sp_pc_do_not_change) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0x33400U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x500U, frame->rel_pc);
+ EXPECT_EQ(0x33500U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
// Verify format frame code.
TEST_F(UnwinderTest, format_frame_static) {
FrameData frame;
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index dd463d1..018b1a9 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -265,6 +265,9 @@
* Returns 0 on success and negative values on failure, for example if |reader|
* cannot supply the right amount of data, or if the number of bytes written to
* data does not match |uncompressed_length|.
+ *
+ * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
+ * uncompressed data.
*/
int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index a4b5dc5..35d0f0b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -908,6 +908,7 @@
std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
+ const bool compute_crc = (crc_out != nullptr);
uint64_t crc = 0;
uint32_t remaining_bytes = compressed_length;
do {
@@ -939,9 +940,8 @@
if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
const size_t write_size = zstream.next_out - &write_buf[0];
if (!writer->Append(&write_buf[0], write_size)) {
- // The file might have declared a bogus length.
- return kInconsistentInformation;
- } else {
+ return kIoError;
+ } else if (compute_crc) {
crc = crc32(crc, &write_buf[0], write_size);
}
@@ -958,7 +958,9 @@
// it ourselves above because there are no additional gains to be made by
// having zlib calculate it for us, since they do it by calling crc32 in
// the same manner that we have above.
- *crc_out = crc;
+ if (compute_crc) {
+ *crc_out = crc;
+ }
if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index dbc14f0..cb72f0e 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -717,6 +717,93 @@
ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
}
+class VectorReader : public zip_archive::Reader {
+ public:
+ VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
+
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ if ((offset + len) < input_.size()) {
+ return false;
+ }
+
+ memcpy(buf, &input_[offset], len);
+ return true;
+ }
+
+ private:
+ const std::vector<uint8_t>& input_;
+};
+
+class VectorWriter : public zip_archive::Writer {
+ public:
+ VectorWriter() : Writer() {}
+
+ bool Append(uint8_t* buf, size_t size) {
+ output_.insert(output_.end(), buf, buf + size);
+ return true;
+ }
+
+ std::vector<uint8_t>& GetOutput() { return output_; }
+
+ private:
+ std::vector<uint8_t> output_;
+};
+
+class BadReader : public zip_archive::Reader {
+ public:
+ BadReader() : Reader() {}
+
+ bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
+};
+
+class BadWriter : public zip_archive::Writer {
+ public:
+ BadWriter() : Writer() {}
+
+ bool Append(uint8_t*, size_t) { return false; }
+};
+
+TEST(ziparchive, Inflate) {
+ const uint32_t compressed_length = kATxtContentsCompressed.size();
+ const uint32_t uncompressed_length = kATxtContents.size();
+
+ const VectorReader reader(kATxtContentsCompressed);
+ {
+ VectorWriter writer;
+ uint64_t crc_out = 0;
+
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(kATxtContents, writer.GetOutput());
+ ASSERT_EQ(0x950821C5u, crc_out);
+ }
+
+ {
+ VectorWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(kATxtContents, writer.GetOutput());
+ }
+
+ {
+ BadWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(kIoError, ret);
+ }
+
+ {
+ BadReader reader;
+ VectorWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(kIoError, ret);
+ ASSERT_EQ(0u, writer.GetOutput().size());
+ }
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 07981fc..a2aa486 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -457,7 +457,7 @@
" -d Dump the log and then exit (don't block)\n"
" -e <expr>, --regex=<expr>\n"
" Only print lines where the log message matches <expr>\n"
- " where <expr> is a regular expression\n"
+ " where <expr> is a Perl-compatible regular expression\n"
// Leave --head undocumented as alias for -m
" -m <count>, --max-count=<count>\n"
" Quit after printing <count> lines. This is meant to be\n"