diff --git a/BUILD b/BUILD index 2e084c500..ab147e5c9 100644 --- a/BUILD +++ b/BUILD @@ -377,6 +377,8 @@ RELATIVE_TEST_PROTOS = [ "google/protobuf/unittest_preserve_unknown_enum.proto", "google/protobuf/unittest_preserve_unknown_enum2.proto", "google/protobuf/unittest_proto3_arena.proto", + "google/protobuf/unittest_proto3_arena_lite.proto", + "google/protobuf/unittest_proto3_lite.proto", "google/protobuf/unittest_well_known_types.proto", "google/protobuf/util/internal/testdata/anys.proto", "google/protobuf/util/internal/testdata/books.proto", @@ -463,6 +465,8 @@ cc_test( "src/google/protobuf/no_field_presence_test.cc", "src/google/protobuf/preserve_unknown_enum_test.cc", "src/google/protobuf/proto3_arena_unittest.cc", + "src/google/protobuf/proto3_arena_lite_unittest.cc", + "src/google/protobuf/proto3_lite_unittest.cc", "src/google/protobuf/reflection_ops_unittest.cc", "src/google/protobuf/repeated_field_reflection_unittest.cc", "src/google/protobuf/repeated_field_unittest.cc", diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs index fd4e65b5b..871a383f0 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs @@ -139,6 +139,8 @@ namespace Google.Protobuf.WellKnownTypes { /// * If no schema is provided, `https` is assumed. /// * The last segment of the URL's path must represent the fully /// qualified name of the type (as in `path/google.protobuf.Duration`). + /// The name should be in a canonical form (e.g., leading "." is + /// not accepted). /// * An HTTP GET on the URL must yield a [google.protobuf.Type][] /// value in binary format, or produce an error. /// * Applications are allowed to cache lookup results based on the diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs index 79a0319fe..6f0a64d6a 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs @@ -86,7 +86,7 @@ namespace Google.Protobuf.WellKnownTypes { /// operation applies to all fields (as if a FieldMask of all fields /// had been specified). /// - /// Note that a field mask does not necessarily applies to the + /// Note that a field mask does not necessarily apply to the /// top-level response message. In case of a REST get operation, the /// field mask applies directly to the response, but in case of a REST /// list operation, the mask instead applies to each individual message diff --git a/docs/swift/DesignDoc.md b/docs/swift/DesignDoc.md new file mode 100644 index 000000000..364a4d3f1 --- /dev/null +++ b/docs/swift/DesignDoc.md @@ -0,0 +1,674 @@ +# Protocol Buffers in Swift + +## Objective + +This document describes the user-facing API and internal implementation of +proto2 and proto3 messages in Apple’s Swift programming language. + +One of the key goals of protobufs is to provide idiomatic APIs for each +language. In that vein, **interoperability with Objective-C is a non-goal of +this proposal.** Protobuf users who need to pass messages between Objective-C +and Swift code in the same application should use the existing Objective-C proto +library. The goal of the effort described here is to provide an API for protobuf +messages that uses features specific to Swift—optional types, algebraic +enumerated types, value types, and so forth—in a natural way that will delight, +rather than surprise, users of the language. + +## Naming + +* By convention, both typical protobuf message names and Swift structs/classes + are `UpperCamelCase`, so for most messages, the name of a message can be the + same as the name of its generated type. (However, see the discussion below + about prefixes under [Packages](#packages).) + +* Enum cases in protobufs typically are `UPPERCASE_WITH_UNDERSCORES`, whereas + in Swift they are `lowerCamelCase` (as of the Swift 3 API design + guidelines). We will transform the names to match Swift convention, using + a whitelist similar to the Objective-C compiler plugin to handle commonly + used acronyms. + +* Typical fields in proto messages are `lowercase_with_underscores`, while in + Swift they are `lowerCamelCase`. We will transform the names to match + Swift convention by removing the underscores and uppercasing the subsequent + letter. + +## Swift reserved words + +Swift has a large set of reserved words—some always reserved and some +contextually reserved (that is, they can be used as identifiers in contexts +where they would not be confused). As of Swift 2.2, the set of always-reserved +words is: + +``` +_, #available, #column, #else, #elseif, #endif, #file, #function, #if, #line, +#selector, as, associatedtype, break, case, catch, class, continue, default, +defer, deinit, do, dynamicType, else, enum, extension, fallthrough, false, for, +func, guard, if, import, in, init, inout, internal, is, let, nil, operator, +private, protocol, public, repeat, rethrows, return, self, Self, static, +struct, subscript, super, switch, throw, throws, true, try, typealias, var, +where, while +``` + +The set of contextually reserved words is: + +``` +associativity, convenience, dynamic, didSet, final, get, infix, indirect, +lazy, left, mutating, none, nonmutating, optional, override, postfix, +precedence, prefix, Protocol, required, right, set, Type, unowned, weak, +willSet +``` + +It is possible to use any reserved word as an identifier by escaping it with +backticks (for example, ``let `class` = 5``). Other name-mangling schemes would +require us to transform the names themselves (for example, by appending an +underscore), which requires us to then ensure that the new name does not collide +with something else in the same namespace. + +While the backtick feature may not be widely known by all Swift developers, a +small amount of user education can address this and it seems like the best +approach. We can unconditionally surround all property names with backticks to +simplify generation. + +Some remapping will still be required, though, to avoid collisions between +generated properties and the names of methods and properties defined in the base +protocol/implementation of messages. + +# Features of Protocol Buffers + +This section describes how the features of the protocol buffer syntaxes (proto2 +and proto3) map to features in Swift—what the code generated from a proto will +look like, and how it will be implemented in the underlying library. + +## Packages + +Modules are the main form of namespacing in Swift, but they are not declared +using syntactic constructs like namespaces in C++ or packages in Java. Instead, +they are tied to build targets in Xcode (or, in the future with open-source +Swift, declarations in a Swift Package Manager manifest). They also do not +easily support nesting submodules (Clang module maps support this, but pure +Swift does not yet provide a way to define submodules). + +We will generate types with fully-qualified underscore-delimited names. For +example, a message `Baz` in package `foo.bar` would generate a struct named +`Foo_Bar_Baz`. For each fully-qualified proto message, there will be exactly one +unique type symbol emitted in the generated binary. + +Users are likely to balk at the ugliness of underscore-delimited names for every +generated type. To improve upon this situation, we will add a new string file +level option, `swift_package_typealias`, that can be added to `.proto` files. +When present, this will cause `typealias`es to be added to the generated Swift +messages that replace the package name prefix with the provided string. For +example, the following `.proto` file: + +```protobuf +option swift_package_typealias = "FBP"; +package foo.bar; + +message Baz { + // Message fields +} +``` + +would generate the following Swift source: + +```swift +public struct Foo_Bar_Baz { + // Message fields and other methods +} + +typealias FBPBaz = Foo_Bar_Baz +``` + +It should be noted that this type alias is recorded in the generated +`.swiftmodule` so that code importing the module can refer to it, but it does +not cause a new symbol to be generated in the compiled binary (i.e., we do not +risk compiled size bloat by adding `typealias`es for every type). + +Other strategies to handle packages that were considered and rejected can be +found in [Appendix A](#appendix-a-rejected-strategies-to-handle-packages). + +## Messages + +Proto messages are natural value types and we will generate messages as structs +instead of classes. Users will benefit from Swift’s built-in behavior with +regard to mutability. We will define a `ProtoMessage` protocol that defines the +common methods and properties for all messages (such as serialization) and also +lets users treat messages polymorphically. Any shared method implementations +that do not differ between individual messages can be implemented in a protocol +extension. + +The backing storage itself for fields of a message will be managed by a +`ProtoFieldStorage` type that uses an internal dictionary keyed by field number, +and whose values are the value of the field with that number (up-cast to Swift’s +`Any` type). This class will provide type-safe getters and setters so that +generated messages can manipulate this storage, and core serialization logic +will live here as well. Furthermore, factoring the storage out into a separate +type, rather than inlining the fields as stored properties in the message +itself, lets us implement copy-on-write efficiently to support passing around +large messages. (Furthermore, because the messages themselves are value types, +inlining fields is not possible if the fields are submessages of the same type, +or a type that eventually includes a submessage of the same type.) + +### Required fields (proto2 only) + +Required fields in proto2 messages seem like they could be naturally represented +by non-optional properties in Swift, but this presents some problems/concerns. + +Serialization APIs permit partial serialization, which allows required fields to +remain unset. Furthermore, other language APIs still provide `has*` and `clear*` +methods for required fields, and knowing whether a property has a value when the +message is in memory is still useful. + +For example, an e-mail draft message may have the “to” address required on the +wire, but when the user constructs it in memory, it doesn’t make sense to force +a value until they provide one. We only want to force a value to be present when +the message is serialized to the wire. Using non-optional properties prevents +this use case, and makes client usage awkward because the user would be forced +to select a sentinel or placeholder value for any required fields at the time +the message was created. + +### Default values + +In proto2, fields can have a default value specified that may be a value other +than the default value for its corresponding language type (for example, a +default value of 5 instead of 0 for an integer). When reading a field that is +not explicitly set, the user expects to get that value. This makes Swift +optionals (i.e., `Foo?`) unsuitable for fields in general. Unfortunately, we +cannot implement our own “enhanced optional” type without severely complicating +usage (Swift’s use of type inference and its lack of implicit conversions would +require manual unwrapping of every property value). + +Instead, we can use **implicitly unwrapped optionals.** For example, a property +generated for a field of type `int32` would have Swift type `Int32!`. These +properties would behave with the following characteristics, which mirror the +nil-resettable properties used elsewhere in Apple’s SDKs (for example, +`UIView.tintColor`): + +* Assigning a non-nil value to a property sets the field to that value. +* Assigning nil to a property clears the field (its internal representation is + nilled out). +* Reading the value of a property returns its value if it is set, or returns + its default value if it is not set. Reading a property never returns nil. + +The final point in the list above implies that the optional cannot be checked to +determine if the field is set to a value other than its default: it will never +be nil. Instead, we must provide `has*` methods for each field to allow the user +to check this. These methods will be public in proto2. In proto3, these methods +will be private (if generated at all), since the user can test the returned +value against the zero value for that type. + +### Autocreation of nested messages + +For convenience, dotting into an unset field representing a nested message will +return an instance of that message with default values. As in the Objective-C +implementation, this does not actually cause the field to be set until the +returned message is mutated. Fortunately, thanks to the way mutability of value +types is implemented in Swift, the language automatically handles the +reassignment-on-mutation for us. A static singleton instance containing default +values can be associated with each message that can be returned when reading, so +copies are only made by the Swift runtime when mutation occurs. For example, +given the following proto: + +```protobuf +message Node { + Node child = 1; + string value = 2 [default = "foo"]; +} +``` + +The following Swift code would act as commented, where setting deeply nested +properties causes the copies and mutations to occur as the assignment statement +is unwound: + +```swift +var node = Node() + +let s = node.child.child.value +// 1. node.child returns the "default Node". +// 2. Reading .child on the result of (1) returns the same default Node. +// 3. Reading .value on the result of (2) returns the default value "foo". + +node.child.child.value = "bar" +// 4. Setting .value on the default Node causes a copy to be made and sets +// the property on that copy. Subsequently, the language updates the +// value of "node.child.child" to point to that copy. +// 5. Updating "node.child.child" in (4) requires another copy, because +// "node.child" was also the instance of the default node. The copy is +// assigned back to "node.child". +// 6. Setting "node.child" in (5) is a simple value reassignment, since +// "node" is a mutable var. +``` + +In other words, the generated messages do not internally have to manage parental +relationships to backfill the appropriate properties on mutation. Swift provides +this for free. + +## Scalar value fields + +Proto scalar value fields will map to Swift types in the following way: + +.proto Type | Swift Type +----------- | ------------------- +`double` | `Double` +`float` | `Float` +`int32` | `Int32` +`int64` | `Int64` +`uint32` | `UInt32` +`uint64` | `UInt64` +`sint32` | `Int32` +`sint64` | `Int64` +`fixed32` | `UInt32` +`fixed64` | `UInt64` +`sfixed32` | `Int32` +`sfixed64` | `Int64` +`bool` | `Bool` +`string` | `String` +`bytes` | `Foundation.NSData` + +The proto spec defines a number of integral types that map to the same Swift +type; for example, `intXX`, `sintXX`, and `sfixedXX` are all signed integers, +and `uintXX` and `fixedXX` are both unsigned integers. No other language +implementation distinguishes these further, so we do not do so either. The +rationale is that the various types only serve to distinguish how the value is +**encoded on the wire**; once loaded in memory, the user is not concerned about +these variations. + +Swift’s lack of implicit conversions among types will make it slightly annoying +to use these types in a context expecting an `Int`, or vice-versa, but since +this is a data-interchange format with explicitly-sized fields, we should not +hide that information from the user. Users will have to explicitly write +`Int(message.myField)`, for example. + +## Embedded message fields + +Embedded message fields can be represented using an optional variable of the +generated message type. Thus, the message + +```protobuf +message Foo { + Bar bar = 1; +} +``` + +would be represented in Swift as + +```swift +public struct Foo: ProtoMessage { + public var bar: Bar! { + get { ... } + set { ... } + } +} +``` + +If the user explicitly sets `bar` to nil, or if it was never set when read from +the wire, retrieving the value of `bar` would return a default, statically +allocated instance of `Bar` containing default values for its fields. This +achieves the desired behavior for default values in the same way that scalar +fields are designed, and also allows users to deep-drill into complex object +graphs to get or set fields without checking for nil at each step. + +## Enum fields + +The design and implementation of enum fields will differ somewhat drastically +depending on whether the message being generated is a proto2 or proto3 message. + +### proto2 enums + +For proto2, we do not need to be concerned about unknown enum values, so we can +use the simple raw-value enum syntax provided by Swift. So the following enum in +proto2: + +```protobuf +enum ContentType { + TEXT = 0; + IMAGE = 1; +} +``` + +would become this Swift enum: + +```swift +public enum ContentType: Int32, NilLiteralConvertible { + case text = 0 + case image = 1 + + public init(nilLiteral: ()) { + self = .text + } +} +``` + +See below for the discussion about `NilLiteralConvertible`. + +### proto3 enums + +For proto3, we need to be able to preserve unknown enum values that may come +across the wire so that they can be written back if unmodified. We can +accomplish this in Swift by using a case with an associated value for unknowns. +So the following enum in proto3: + +```protobuf +enum ContentType { + TEXT = 0; + IMAGE = 1; +} +``` + +would become this Swift enum: + +```swift +public enum ContentType: RawRepresentable, NilLiteralConvertible { + case text + case image + case UNKNOWN_VALUE(Int32) + + public typealias RawValue = Int32 + + public init(nilLiteral: ()) { + self = .text + } + + public init(rawValue: RawValue) { + switch rawValue { + case 0: self = .text + case 1: self = .image + default: self = .UNKNOWN_VALUE(rawValue) + } + + public var rawValue: RawValue { + switch self { + case .text: return 0 + case .image: return 1 + case .UNKNOWN_VALUE(let value): return value + } + } +} +``` + +Note that the use of a parameterized case prevents us from inheriting from the +raw `Int32` type; Swift does not allow an enum with a raw type to have cases +with arguments. Instead, we must implement the raw value initializer and +computed property manually. The `UNKNOWN_VALUE` case is explicitly chosen to be +"ugly" so that it stands out and does not conflict with other possible case +names. + +Using this approach, proto3 consumers must always have a default case or handle +the `.UNKNOWN_VALUE` case to satisfy case exhaustion in a switch statement; the +Swift compiler considers it an error if switch statements are not exhaustive. + +### NilLiteralConvertible conformance + +This is required to clean up the usage of enum-typed properties in switch +statements. Unlike other field types, enum properties cannot be +implicitly-unwrapped optionals without requiring that uses in switch statements +be explicitly unwrapped. For example, if we consider a message with the enum +above, this usage will fail to compile: + +```swift +// Without NilLiteralConvertible conformance on ContentType +public struct SomeMessage: ProtoMessage { + public var contentType: ContentType! { ... } +} + +// ERROR: no case named text or image +switch someMessage.contentType { + case .text: { ... } + case .image: { ... } +} +``` + +Even though our implementation guarantees that `contentType` will never be nil, +if it is an optional type, its cases would be `some` and `none`, not the cases +of the underlying enum type. In order to use it in this context, the user must +write `someMessage.contentType!` in their switch statement. + +Making the enum itself `NilLiteralConvertible` permits us to make the property +non-optional, so the user can still set it to nil to clear it (i.e., reset it to +its default value), while eliminating the need to explicitly unwrap it in a +switch statement. + +```swift +// With NilLiteralConvertible conformance on ContentType +public struct SomeMessage: ProtoMessage { + // Note that the property type is no longer optional + public var contentType: ContentType { ... } +} + +// OK: Compiles and runs as expected +switch someMessage.contentType { + case .text: { ... } + case .image: { ... } +} + +// The enum can be reset to its default value this way +someMessage.contentType = nil +``` + +One minor oddity with this approach is that nil will be auto-converted to the +default value of the enum in any context, not just field assignment. In other +words, this is valid: + +```swift +func foo(contentType: ContentType) { ... } +foo(nil) // Inside foo, contentType == .text +``` + +That being said, the advantage of being able to simultaneously support +nil-resettability and switch-without-unwrapping outweighs this side effect, +especially if appropriately documented. It is our hope that a new form of +resettable properties will be added to Swift that eliminates this inconsistency. +Some community members have already drafted or sent proposals for review that +would benefit our designs: + +* [SE-0030: Property Behaviors] + (https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md) +* [Drafted: Resettable Properties] + (https://github.com/patters/swift-evolution/blob/master/proposals/0000-resettable-properties.md) + +### Enum aliases + +The `allow_alias` option in protobuf slightly complicates the use of Swift enums +to represent that type, because raw values of cases in an enum must be unique. +Swift lets us define static variables in an enum that alias actual cases. For +example, the following protobuf enum: + +```protobuf +enum Foo { + option allow_alias = true; + BAR = 0; + BAZ = 0; +} +``` + +will be represented in Swift as: + +```swift +public enum Foo: Int32, NilLiteralConvertible { + case bar = 0 + static public let baz = bar + + // ... etc. +} + +// Can still use .baz shorthand to reference the alias in contexts +// where the type is inferred +``` + +That is, we use the first name as the actual case and use static variables for +the other aliases. One drawback to this approach is that the static aliases +cannot be used as cases in a switch statement (the compiler emits the error +*“Enum case ‘baz’ not found in type ‘Foo’”*). However, in our own code bases, +there are only a few places where enum aliases are not mere renamings of an +older value, but they also don’t appear to be the type of value that one would +expect to switch on (for example, a group of named constants representing +metrics rather than a set of options), so this restriction is not significant. + +This strategy also implies that changing the name of an enum and adding the old +name as an alias below the new name will be a breaking change in the generated +Swift code. + +## Oneof types + +The `oneof` feature represents a “variant/union” data type that maps nicely to +Swift enums with associated values (algebraic types). These fields can also be +accessed independently though, and, specifically in the case of proto2, it’s +reasonable to expect access to default values when accessing a field that is not +explicitly set. + +Taking all this into account, we can represent a `oneof` in Swift with two sets +of constructs: + +* Properties in the message that correspond to the `oneof` fields. +* A nested enum named after the `oneof` and which provides the corresponding + field values as case arguments. + +This approach fulfills the needs of proto consumers by providing a +Swift-idiomatic way of simultaneously checking which field is set and accessing +its value, providing individual properties to access the default values +(important for proto2), and safely allows a field to be moved into a `oneof` +without breaking clients. + +Consider the following proto: + +```protobuf +message MyMessage { + oneof record { + string name = 1 [default = "unnamed"]; + int32 id_number = 2 [default = 0]; + } +} +``` + +In Swift, we would generate an enum, a property for that enum, and properties +for the fields themselves: + +```swift +public struct MyMessage: ProtoMessage { + public enum Record: NilLiteralConvertible { + case name(String) + case idNumber(Int32) + case NOT_SET + + public init(nilLiteral: ()) { self = .NOT_SET } + } + + // This is the "Swifty" way of accessing the value + public var record: Record { ... } + + // Direct access to the underlying fields + public var name: String! { ... } + public var idNumber: Int32! { ... } +} +``` + +This makes both usage patterns possible: + +```swift +// Usage 1: Case-based dispatch +switch message.record { + case .name(let name): + // Do something with name if it was explicitly set + case .idNumber(let id): + // Do something with id_number if it was explicitly set + case .NOT_SET: + // Do something if it’s not set +} + +// Usage 2: Direct access for default value fallback +// Sets the label text to the name if it was explicitly set, or to +// "unnamed" (the default value for the field) if id_number was set +// instead +let myLabel = UILabel() +myLabel.text = message.name +``` + +As with proto enums, the generated `oneof` enum conforms to +`NilLiteralConvertible` to avoid switch statement issues. Setting the property +to nil will clear it (i.e., reset it to `NOT_SET`). + +## Unknown Fields (proto2 only) + +To be written. + +## Extensions (proto2 only) + +To be written. + +## Reflection and Descriptors + +We will not include reflection or descriptors in the first version of the Swift +library. The use cases for reflection on mobile are not as strong and the static +data to represent the descriptors would add bloat when we wish to keep the code +size small. + +In the future, we will investigate whether they can be included as extensions +which might be able to be excluded from a build and/or automatically dead +stripped by the compiler if they are not used. + +## Appendix A: Rejected strategies to handle packages + +### Each package is its own Swift module + +Each proto package could be declared as its own Swift module, replacing dots +with underscores (e.g., package `foo.bar` becomes module `Foo_Bar`). Then, users +would simply import modules containing whatever proto modules they want to use +and refer to the generated types by their short names. + +**This solution is simply not possible, however.** Swift modules cannot +circularly reference each other, but there is no restriction against proto +packages doing so. Circular imports are forbidden (e.g., `foo.proto` importing +`bar.proto` importing `foo.proto`), but nothing prevents package `foo` from +using a type in package `bar` which uses a different type in package `foo`, as +long as there is no import cycle. If these packages were generated as Swift +modules, then `Foo` would contain an `import Bar` statement and `Bar` would +contain an `import Foo` statement, and there is no way to compile this. + +### Ad hoc namespacing with structs + +We can “fake” namespaces in Swift by declaring empty structs with private +initializers. Since modules are constructed based on compiler arguments, not by +syntactic constructs, and because there is no pure Swift way to define +submodules (even though Clang module maps support this), there is no +source-drive way to group generated code into namespaces aside from this +approach. + +Types can be added to those intermediate package structs using Swift extensions. +For example, a message `Baz` in package `foo.bar` could be represented in Swift +as follows: + +```swift +public struct Foo { + private init() {} +} + +public extension Foo { + public struct Bar { + private init() {} + } +} + +public extension Foo.Bar { + public struct Baz { + // Message fields and other methods + } +} + +let baz = Foo.Bar.Baz() +``` + +Each of these constructs would actually be defined in a separate file; Swift +lets us keep them separate and add multiple structs to a single “namespace” +through extensions. + +Unfortunately, these intermediate structs generate symbols of their own +(metatype information in the data segment). This becomes problematic if multiple +build targets contain Swift sources generated from different messages in the +same package. At link time, these symbols would collide, resulting in multiple +definition errors. + +This approach also has the disadvantage that there is no automatic “short” way +to refer to the generated messages at the deepest nesting levels; since this use +of structs is a hack around the lack of namespaces, there is no equivalent to +import (Java) or using (C++) to simplify this. Users would have to declare type +aliases to make this cleaner, or we would have to generate them for users. diff --git a/docs/third_party.md b/docs/third_party.md new file mode 100644 index 000000000..69ed6a386 --- /dev/null +++ b/docs/third_party.md @@ -0,0 +1,147 @@ +# Third-Party Add-ons for Protocol Buffers + +This page lists code related to Protocol Buffers which is developed and maintained by third parties. You may find this code useful, but note that **these projects are not affiliated with or endorsed by Google (unless explicitly marked)**; try them at your own risk. Also note that many projects here are in the early stages of development and not production-ready. + +If you have a project that should be listed here, please [send us a pull request](https://github.com/google/protobuf/pulls) to update this page. + +## Programming Languages + +These are projects we know about implementing Protocol Buffers for other programming languages: +* Action Script: http://code.google.com/p/protobuf-actionscript3/ +* Action Script: https://code.google.com/p/protoc-gen-as3/ +* Action Script: https://github.com/matrix3d/JProtoc +* C: https://github.com/protobuf-c/protobuf-c +* C: http://koti.kapsi.fi/jpa/nanopb/ +* C: https://github.com/cloudwu/pbc/ +* C: https://github.com/haberman/upb/wiki +* C: https://github.com/squidfunk/protobluff +* C++: https://github.com/google/protobuf (Google-official implementation) +* C/C++: http://spbc.sf.net/ +* C#: http://code.google.com/p/protobuf-csharp-port +* C#: http://code.google.com/p/protosharp/ +* C#: https://silentorbit.com/protobuf/ +* C#/.NET/WCF/VB: http://code.google.com/p/protobuf-net/ +* Clojure: http://github.com/ninjudd/clojure-protobuf +* Common Lisp: http://www.prism.gatech.edu/~ndantam3/docs/s-protobuf/ +* Common Lisp: http://github.com/brown/protobuf +* D: https://github.com/msoucy/dproto +* D: http://256.makerslocal.org/wiki/index.php/ProtocolBuffer +* D: https://github.com/opticron/ProtocolBuffer +* Dart: https://github.com/dart-lang/dart-protobuf (runtime) https://github.com/dart-lang/dart-protoc-plugin (code generator) +* Delphi: http://sourceforge.net/projects/protobuf-delphi/ +* Delphi: http://fundementals.sourceforge.net/dl.html +* Elixir: https://github.com/jeremyong/exprotoc +* Erlang: http://github.com/ngerakines/erlang_protobuffs/tree/master +* Erlang: http://piqi.org/ +* Erlang: https://code.google.com/p/protoc-gen-erl/ +* Erlang: https://github.com/basho/erlang_protobuffs +* Go: https://github.com/golang/protobuf (Google-official implementation) +* Go: http://code.google.com/p/goprotobuf/ +* Go: https://github.com/akunspy/gopbuf +* Haskell: http://hackage.haskell.org/package/hprotoc +* Haxe: https://github.com/Atry/protoc-gen-haxe +* Java: https://github.com/google/protobuf (Google-official implementation) +* Java/Android: https://github.com/square/wire +* Java ME: http://code.google.com/p/protobuf-javame/ +* Java ME: http://swingme.sourceforge.net/encode.shtml +* Java ME: http://github.com/ponderingpanda/protobuf-j2me +* Java ME: http://code.google.com/p/protobuf-j2me/ +* Javascript: http://code.google.com/p/protobuf-js/ +* Javascript: http://github.com/sirikata/protojs +* Javascript: https://github.com/dcodeIO/ProtoBuf.js +* Javascript: http://code.google.com/p/protobuf-for-node/ +* Javascript: http://code.google.com/p/protostuff/ +* Julia: https://github.com/tanmaykm/ProtoBuf.jl +* Lua: http://code.google.com/p/protoc-gen-lua/ +* Lua: http://github.com/indygreg/lua-protobuf +* Lua: https://github.com/Neopallium/lua-pb +* Matlab: http://code.google.com/p/protobuf-matlab/ +* Mercury: http://code.google.com/p/protobuf-mercury/ +* Objective C: http://code.google.com/p/protobuf-objc/ +* Objective C: https://github.com/alexeyxo/protobuf-objc +* OCaml: http://piqi.org/ +* Perl: http://groups.google.com/group/protobuf-perl +* Perl: http://search.cpan.org/perldoc?Google::ProtocolBuffers +* Perl/XS: http://code.google.com/p/protobuf-perlxs/ +* PHP: http://code.google.com/p/pb4php/ +* PHP: https://github.com/allegro/php-protobuf/ +* PHP: https://github.com/chobie/php-protocolbuffers +* PHP: http://drslump.github.com/Protobuf-PHP +* Prolog: http://www.swi-prolog.org/pldoc/package/protobufs.html +* Python: https://github.com/google/protobuf (Google-official implementation) +* Python: http://eigenein.github.com/protobuf/ +* R: http://cran.r-project.org/package=RProtoBuf +* Ruby: http://code.google.com/p/ruby-protobuf/ +* Ruby: http://github.com/mozy/ruby-protocol-buffers +* Ruby: https://github.com/bmizerany/beefcake/tree/master/lib/beefcake +* Ruby: https://github.com/localshred/protobuf +* Rust: https://github.com/stepancheg/rust-protobuf/ +* Scala: http://github.com/jeffplaisance/scala-protobuf +* Scala: http://code.google.com/p/protobuf-scala +* Scala: https://github.com/SandroGrzicic/ScalaBuff +* Scala: http://trueaccord.github.io/ScalaPB/ +* Swift: https://github.com/alexeyxo/protobuf-swift +* Vala: https://launchpad.net/protobuf-vala +* Visual Basic: http://code.google.com/p/protobuf-net/ + +## RPC Implementations + +GRPC (http://www.grpc.io/) is Google's RPC implementation for Protocol Buffers. There are other third-party RPC implementations as well. Some of these actually work with Protocol Buffers service definitions (defined using the `service` keyword in `.proto` files) while others just use Protocol Buffers message objects. + +* https://github.com/grpc/grpc (C++, Node.js, Python, Ruby, Objective-C, PHP, C#, Google-official implementation) +* http://zeroc.com/ice.html (Multiple languages) +* http://code.google.com/p/protobuf-net/ (C#/.NET/WCF/VB) +* https://launchpad.net/txprotobuf/ (Python) +* https://github.com/modeswitch/protobuf-rpc (Python) +* http://code.google.com/p/protobuf-socket-rpc/ (Java, Python) +* http://code.google.com/p/proto-streamer/ (Java) +* http://code.google.com/p/server1/ (C++) +* http://deltavsoft.com/RcfUserGuide/Protobufs (C++) +* http://code.google.com/p/protobuf-mina-rpc/ (Python client, Java server) +* http://code.google.com/p/casocklib/ (C++) +* http://code.google.com/p/cxf-protobuf/ (Java) +* http://code.google.com/p/protobuf-remote/ (C++/C#) +* http://code.google.com/p/protobuf-rpc-pro/ (Java) +* https://code.google.com/p/protorpc/ (Go/C++) +* https://code.google.com/p/eneter-protobuf-serializer/ (Java/.NET) +* http://www.deltavsoft.com/RCFProto.html (C++/Java/Python/C#) +* https://github.com/robbinfan/claire-protorpc (C++) +* https://github.com/BaiduPS/sofa-pbrpc (C++) +* https://github.com/ebencheung/arab (C++) +* http://code.google.com/p/protobuf-csharp-rpc/ (C#) +* https://github.com/thesamet/rpcz (C++/Python, based on ZeroMQ) +* https://github.com/w359405949/libmaid (C++, Python) +* https://github.com/madwyn/libpbrpc (C++) + +## Other Utilities + +There are miscellaneous other things you may find useful as a Protocol Buffers developer. + +* [NetBeans IDE plugin](http://code.google.com/p/protobuf-netbeans-plugin/) +* [Wireshark/Ethereal packet sniffer plugin](http://code.google.com/p/protobuf-wireshark/) +* [Alternate encodings (JSON, XML, HTML) for Java protobufs](http://code.google.com/p/protobuf-java-format/) +* [Another JSON encoder/decoder for Java](https://github.com/sijuv/protobuf-codec) +* [Editor for serialized protobufs](http://code.google.com/p/protobufeditor/) +* [Intellij IDEA plugin](http://github.com/nnmatveev/idea-plugin-protobuf) +* [TextMate syntax highlighting](http://github.com/michaeledgar/protobuf-tmbundle) +* [Oracle PL SQL plugin](http://code.google.com/p/protocol-buffer-plsql/) +* [Eclipse editor for protobuf (from Google)](http://code.google.com/p/protobuf-dt/) +* [C++ Builder compatible protobuf](https://github.com/saadware/protobuf-cppbuilder) +* Maven Protocol Compiler Plugin + * https://github.com/sergei-ivanov/maven-protoc-plugin/ + * http://igor-petruk.github.com/protobuf-maven-plugin/ + * http://code.google.com/p/maven-protoc-plugin/ + * https://github.com/os72/protoc-jar-maven-plugin +* [Documentation generator plugin (Markdown/HTML/DocBook/...)](https://github.com/estan/protoc-gen-doc) +* [DocBook generator for .proto files](http://code.google.com/p/protoc-gen-docbook/) +* [Protobuf for nginx module](https://github.com/dbcode/protobuf-nginx/) +* [RSpec matchers and Cucumber step defs for testing Protocol Buffers](https://github.com/connamara/protobuf_spec) +* [Sbt plugin for Protocol Buffers](https://github.com/Atry/sbt-cppp) +* [Gradle Protobuf Plugin](https://github.com/aantono/gradle-plugin-protobuf) +* [Multi-platform executable JAR and Java API for protoc](https://github.com/os72/protoc-jar) +* [Python scripts to convert between Protocol Buffers and JSON](https://github.com/NextTuesday/py-pb-converters) +* [Visual Studio Language Service support for Protocol Buffers](http://visualstudiogallery.msdn.microsoft.com/4bc0f38c-b058-4e05-ae38-155e053c19c5) +* [C++ library for serialization/de-serialization between Protocol Buffers and JSON.](https://github.com/yinqiwen/pbjson) +* [ProtoBuf with Java EE7 Expression Language 3.0; pure Java ProtoBuf Parser and Builder.](https://github.com/protobufel/protobuf-el) +* [Notepad++ Syntax Highlighting for .proto files](https://github.com/chai2010/notepadplus-protobuf) +* [Linter for .proto files](https://github.com/ckaznocha/protoc-gen-lint) diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java index 57e732a02..790cb622a 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -797,6 +797,8 @@ public abstract class GeneratedMessage extends AbstractMessage extends GeneratedMessage implements ExtendableMessageOrBuilder { + private static final long serialVersionUID = 1L; + private final FieldSet extensions; protected ExtendableMessage() { diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index 0e1599dcc..8134e2596 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -2603,9 +2603,13 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( size_t fieldOffset = field->description_->offset; switch (fieldDataType) { case GPBDataTypeBool: { - BOOL *selfValPtr = (BOOL *)&selfStorage[fieldOffset]; - BOOL *otherValPtr = (BOOL *)&otherStorage[fieldOffset]; - if (*selfValPtr != *otherValPtr) { + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL selfValue = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + BOOL otherValue = GPBGetHasIvar(other, (int32_t)(fieldOffset), 0); + if (selfValue != otherValue) { return NO; } break; @@ -2714,8 +2718,12 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( size_t fieldOffset = field->description_->offset; switch (fieldDataType) { case GPBDataTypeBool: { - BOOL *valPtr = (BOOL *)&storage[fieldOffset]; - result = prime * result + *valPtr; + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL value = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + result = prime * result + value; break; } case GPBDataTypeSFixed32: diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m index 43546156c..89d1fce2c 100644 --- a/objectivec/Tests/GPBMessageTests.m +++ b/objectivec/Tests/GPBMessageTests.m @@ -1947,4 +1947,76 @@ EnumTestMsg_MyEnum_NegTwo); } +- (void)testOneBasedEnumHolder { + // Test case for https://github.com/google/protobuf/issues/1453 + // Message with no explicit defaults, but a non zero default for an enum. + MessageWithOneBasedEnum *enumMsg = [MessageWithOneBasedEnum message]; + XCTAssertEqual(enumMsg.enumField, MessageWithOneBasedEnum_OneBasedEnum_One); +} + +- (void)testBoolOffsetUsage { + // Bools use storage within has_bits; this test ensures that this is honored + // in all places where things should crash or fail based on reading out of + // field storage instead. + BoolOnlyMessage *msg1 = [BoolOnlyMessage message]; + BoolOnlyMessage *msg2 = [BoolOnlyMessage message]; + + msg1.boolField1 = YES; + msg2.boolField1 = YES; + msg1.boolField3 = YES; + msg2.boolField3 = YES; + msg1.boolField5 = YES; + msg2.boolField5 = YES; + msg1.boolField7 = YES; + msg2.boolField7 = YES; + msg1.boolField9 = YES; + msg2.boolField9 = YES; + msg1.boolField11 = YES; + msg2.boolField11 = YES; + msg1.boolField13 = YES; + msg2.boolField13 = YES; + msg1.boolField15 = YES; + msg2.boolField15 = YES; + msg1.boolField17 = YES; + msg2.boolField17 = YES; + msg1.boolField19 = YES; + msg2.boolField19 = YES; + msg1.boolField21 = YES; + msg2.boolField21 = YES; + msg1.boolField23 = YES; + msg2.boolField23 = YES; + msg1.boolField25 = YES; + msg2.boolField25 = YES; + msg1.boolField27 = YES; + msg2.boolField27 = YES; + msg1.boolField29 = YES; + msg2.boolField29 = YES; + msg1.boolField31 = YES; + msg2.boolField31 = YES; + + msg1.boolField32 = YES; + msg2.boolField32 = YES; + + XCTAssertTrue(msg1 != msg2); // Different pointers. + XCTAssertEqual([msg1 hash], [msg2 hash]); + XCTAssertEqualObjects(msg1, msg2); + + BoolOnlyMessage *msg1Prime = [[msg1 copy] autorelease]; + XCTAssertTrue(msg1Prime != msg1); // Different pointers. + XCTAssertEqual([msg1 hash], [msg1Prime hash]); + XCTAssertEqualObjects(msg1, msg1Prime); + + // Field set in one, but not the other means they don't match (even if + // set to default value). + msg1Prime.boolField2 = NO; + XCTAssertNotEqualObjects(msg1Prime, msg1); + // And when set to different values. + msg1.boolField2 = YES; + XCTAssertNotEqualObjects(msg1Prime, msg1); + // And then they match again. + msg1.boolField2 = NO; + XCTAssertEqualObjects(msg1Prime, msg1); + XCTAssertEqual([msg1 hash], [msg1Prime hash]); +} + @end diff --git a/objectivec/Tests/unittest_objc.proto b/objectivec/Tests/unittest_objc.proto index 9483cb1d7..f6ab6a24c 100644 --- a/objectivec/Tests/unittest_objc.proto +++ b/objectivec/Tests/unittest_objc.proto @@ -401,3 +401,49 @@ message EnumTestMsg { repeated MyEnum mumble = 4; } + +// Test case for https://github.com/google/protobuf/issues/1453 +// Message with no explicit defaults, but a non zero default for an enum. +message MessageWithOneBasedEnum { + enum OneBasedEnum { + ONE = 1; + TWO = 2; + } + optional OneBasedEnum enum_field = 1; +} + +// Message with all bools for testing things related to bool storage. +message BoolOnlyMessage { + optional bool bool_field_1 = 1; + optional bool bool_field_2 = 2; + optional bool bool_field_3 = 3; + optional bool bool_field_4 = 4; + optional bool bool_field_5 = 5; + optional bool bool_field_6 = 6; + optional bool bool_field_7 = 7; + optional bool bool_field_8 = 8; + optional bool bool_field_9 = 9; + optional bool bool_field_10 = 10; + optional bool bool_field_11 = 11; + optional bool bool_field_12 = 12; + optional bool bool_field_13 = 13; + optional bool bool_field_14 = 14; + optional bool bool_field_15 = 15; + optional bool bool_field_16 = 16; + optional bool bool_field_17 = 17; + optional bool bool_field_18 = 18; + optional bool bool_field_19 = 19; + optional bool bool_field_20 = 20; + optional bool bool_field_21 = 21; + optional bool bool_field_22 = 22; + optional bool bool_field_23 = 23; + optional bool bool_field_24 = 24; + optional bool bool_field_25 = 25; + optional bool bool_field_26 = 26; + optional bool bool_field_27 = 27; + optional bool bool_field_28 = 28; + optional bool bool_field_29 = 29; + optional bool bool_field_30 = 30; + optional bool bool_field_31 = 31; + optional bool bool_field_32 = 32; +} diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 9bc7273e0..f6bea50f3 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -656,7 +656,6 @@ static const upb_json_parsermethod *msgdef_jsonparsermethod(Descriptor* desc) { #define STACK_ENV_STACKBYTES 4096 typedef struct { upb_env env; - upb_seededalloc alloc; const char* ruby_error_template; char allocbuf[STACK_ENV_STACKBYTES]; } stackenv; @@ -681,16 +680,12 @@ static bool env_error_func(void* ud, const upb_status* status) { static void stackenv_init(stackenv* se, const char* errmsg) { se->ruby_error_template = errmsg; - upb_env_init(&se->env); - upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); - upb_env_setallocfunc( - &se->env, upb_seededalloc_getallocfunc(&se->alloc), &se->alloc); + upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL); upb_env_seterrorfunc(&se->env, env_error_func, se); } static void stackenv_uninit(stackenv* se) { upb_env_uninit(&se->env); - upb_seededalloc_uninit(&se->alloc); } /* diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 3a51fe471..e16250f3b 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -151,32 +151,30 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { name_len--; } - // Check for a oneof name first. - o = upb_msgdef_ntoo(self->descriptor->msgdef, - name, name_len); + // See if this name corresponds to either a oneof or field in this message. + if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f, + &o)) { + return rb_call_super(argc, argv); + } + if (o != NULL) { + // This is a oneof -- return which field inside the oneof is set. if (setter) { rb_raise(rb_eRuntimeError, "Oneof accessors are read-only."); } return which_oneof_field(self, o); - } - - // Otherwise, check for a field with that name. - f = upb_msgdef_ntof(self->descriptor->msgdef, - name, name_len); - - if (f == NULL) { - return rb_call_super(argc, argv); - } - - if (setter) { - if (argc < 2) { - rb_raise(rb_eArgError, "No value provided to setter."); - } - layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); - return Qnil; } else { - return layout_get(self->descriptor->layout, Message_data(self), f); + // This is a field -- get or set the field's value. + assert(f); + if (setter) { + if (argc < 2) { + rb_raise(rb_eArgError, "No value provided to setter."); + } + layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); + return Qnil; + } else { + return layout_get(self->descriptor->layout, Message_data(self), f); + } } } diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index 212f1e0e6..74a2a1db3 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -12,7 +12,7 @@ typedef struct { } str_t; static str_t *newstr(const char *data, size_t len) { - str_t *ret = malloc(sizeof(*ret) + len); + str_t *ret = upb_gmalloc(sizeof(*ret) + len); if (!ret) return NULL; ret->len = len; memcpy(ret->str, data, len); @@ -20,7 +20,7 @@ static str_t *newstr(const char *data, size_t len) { return ret; } -static void freestr(str_t *s) { free(s); } +static void freestr(str_t *s) { upb_gfree(s); } /* isalpha() etc. from are locale-dependent, which we don't want. */ static bool upb_isbetween(char c, char low, char high) { @@ -65,6 +65,22 @@ static bool upb_isident(const char *str, size_t len, bool full, upb_status *s) { return !start; } +static bool upb_isoneof(const upb_refcounted *def) { + return def->vtbl == &upb_oneofdef_vtbl; +} + +static bool upb_isfield(const upb_refcounted *def) { + return def->vtbl == &upb_fielddef_vtbl; +} + +static const upb_oneofdef *upb_trygetoneof(const upb_refcounted *def) { + return upb_isoneof(def) ? (const upb_oneofdef*)def : NULL; +} + +static const upb_fielddef *upb_trygetfield(const upb_refcounted *def) { + return upb_isfield(def) ? (const upb_fielddef*)def : NULL; +} + /* upb_def ********************************************************************/ @@ -88,9 +104,18 @@ const char *upb_def_name(const upb_def *d) { bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) { assert(!upb_def_isfrozen(def)); - if (!upb_isident(fullname, strlen(fullname), true, s)) return false; - free((void*)def->fullname); - def->fullname = upb_strdup(fullname); + if (!upb_isident(fullname, strlen(fullname), true, s)) { + return false; + } + + fullname = upb_gstrdup(fullname); + if (!fullname) { + upb_upberr_setoom(s); + return false; + } + + upb_gfree((void*)def->fullname); + def->fullname = fullname; return true; } @@ -123,7 +148,7 @@ static bool upb_def_init(upb_def *def, upb_deftype_t type, } static void upb_def_uninit(upb_def *def) { - free((void*)def->fullname); + upb_gfree((void*)def->fullname); } static const char *msgdef_name(const upb_msgdef *m) { @@ -260,8 +285,19 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { int i; uint32_t selector; int n = upb_msgdef_numfields(m); - upb_fielddef **fields = malloc(n * sizeof(*fields)); - if (!fields) return false; + upb_fielddef **fields; + + if (n == 0) { + m->selector_count = UPB_STATIC_SELECTOR_COUNT; + m->submsg_field_count = 0; + return true; + } + + fields = upb_gmalloc(n * sizeof(*fields)); + if (!fields) { + upb_upberr_setoom(s); + return false; + } m->submsg_field_count = 0; for(i = 0, upb_msg_field_begin(&j, m); @@ -270,7 +306,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { upb_fielddef *f = upb_msg_iter_field(&j); assert(f->msg.def == m); if (!upb_validate_field(f, s)) { - free(fields); + upb_gfree(fields); return false; } if (upb_fielddef_issubmsg(f)) { @@ -330,7 +366,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { #undef TRY #endif - free(fields); + upb_gfree(fields); return true; } @@ -407,21 +443,26 @@ static void upb_enumdef_free(upb_refcounted *r) { upb_inttable_iter i; upb_inttable_begin(&i, &e->iton); for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) { - /* To clean up the upb_strdup() from upb_enumdef_addval(). */ - free(upb_value_getcstr(upb_inttable_iter_value(&i))); + /* To clean up the upb_gstrdup() from upb_enumdef_addval(). */ + upb_gfree(upb_value_getcstr(upb_inttable_iter_value(&i))); } upb_strtable_uninit(&e->ntoi); upb_inttable_uninit(&e->iton); upb_def_uninit(upb_enumdef_upcast_mutable(e)); - free(e); + upb_gfree(e); } +const struct upb_refcounted_vtbl upb_enumdef_vtbl = {NULL, &upb_enumdef_free}; + upb_enumdef *upb_enumdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free}; - upb_enumdef *e = malloc(sizeof(*e)); + upb_enumdef *e = upb_gmalloc(sizeof(*e)); if (!e) return NULL; - if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner)) + + if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, + &upb_enumdef_vtbl, owner)) { goto err2; + } + if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2; if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1; return e; @@ -429,7 +470,7 @@ upb_enumdef *upb_enumdef_new(const void *owner) { err1: upb_strtable_uninit(&e->ntoi); err2: - free(e); + upb_gfree(e); return NULL; } @@ -468,27 +509,36 @@ bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, upb_status *status) { + char *name2; + if (!upb_isident(name, strlen(name), false, status)) { return false; } + if (upb_enumdef_ntoiz(e, name, NULL)) { upb_status_seterrf(status, "name '%s' is already defined", name); return false; } + if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) { upb_status_seterrmsg(status, "out of memory"); return false; } - if (!upb_inttable_lookup(&e->iton, num, NULL) && - !upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) { - upb_status_seterrmsg(status, "out of memory"); - upb_strtable_remove(&e->ntoi, name, NULL); - return false; + + if (!upb_inttable_lookup(&e->iton, num, NULL)) { + name2 = upb_gstrdup(name); + if (!name2 || !upb_inttable_insert(&e->iton, num, upb_value_cstr(name2))) { + upb_status_seterrmsg(status, "out of memory"); + upb_strtable_remove(&e->ntoi, name, NULL); + return false; + } } + if (upb_enumdef_numvals(e) == 1) { bool ok = upb_enumdef_setdefault(e, num, NULL); UPB_ASSERT_VAR(ok, ok); } + return true; } @@ -575,9 +625,9 @@ static void freefield(upb_refcounted *r) { upb_fielddef *f = (upb_fielddef*)r; upb_fielddef_uninit_default(f); if (f->subdef_is_symbolic) - free(f->sub.name); + upb_gfree(f->sub.name); upb_def_uninit(upb_fielddef_upcast_mutable(f)); - free(f); + upb_gfree(f); } static const char *enumdefaultstr(const upb_fielddef *f) { @@ -633,12 +683,14 @@ static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) { return false; } +const struct upb_refcounted_vtbl upb_fielddef_vtbl = {visitfield, freefield}; + upb_fielddef *upb_fielddef_new(const void *o) { - static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield}; - upb_fielddef *f = malloc(sizeof(*f)); + upb_fielddef *f = upb_gmalloc(sizeof(*f)); if (!f) return NULL; - if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) { - free(f); + if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, + &upb_fielddef_vtbl, o)) { + upb_gfree(f); return NULL; } f->msg.def = NULL; @@ -689,7 +741,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL; } if (srcname) { - char *newname = malloc(strlen(f->sub.def->fullname) + 2); + char *newname = upb_gmalloc(strlen(f->sub.def->fullname) + 2); if (!newname) { upb_fielddef_unref(newf, owner); return NULL; @@ -697,7 +749,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { strcpy(newname, "."); strcat(newname, f->sub.def->fullname); upb_fielddef_setsubdefname(newf, newname, NULL); - free(newname); + upb_gfree(newname); } return newf; @@ -804,11 +856,12 @@ const char *upb_fielddef_containingtypename(upb_fielddef *f) { } static void release_containingtype(upb_fielddef *f) { - if (f->msg_is_symbolic) free(f->msg.name); + if (f->msg_is_symbolic) upb_gfree(f->msg.name); } bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, upb_status *s) { + char *name_copy; assert(!upb_fielddef_isfrozen(f)); if (upb_fielddef_containingtype(f)) { upb_status_seterrmsg(s, "field has already been added to a message."); @@ -816,8 +869,15 @@ bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, } /* TODO: validate name (upb_isident() doesn't quite work atm because this name * may have a leading "."). */ + + name_copy = upb_gstrdup(name); + if (!name_copy) { + upb_upberr_setoom(s); + return false; + } + release_containingtype(f); - f->msg.name = upb_strdup(name); + f->msg.name = name_copy; f->msg_is_symbolic = true; return true; } @@ -1218,7 +1278,7 @@ static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef, static void release_subdef(upb_fielddef *f) { if (f->subdef_is_symbolic) { - free(f->sub.name); + upb_gfree(f->sub.name); } else if (f->sub.def) { upb_unref2(f->sub.def, f); } @@ -1248,15 +1308,23 @@ bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef, bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, upb_status *s) { + char *name_copy; assert(!upb_fielddef_isfrozen(f)); if (!upb_fielddef_hassubdef(f)) { upb_status_seterrmsg(s, "field type does not accept a subdef"); return false; } + + name_copy = upb_gstrdup(name); + if (!name_copy) { + upb_upberr_setoom(s); + return false; + } + /* TODO: validate name (upb_isident() doesn't quite work atm because this name * may have a leading "."). */ release_subdef(f); - f->sub.name = upb_strdup(name); + f->sub.name = name_copy; f->subdef_is_symbolic = true; return true; } @@ -1332,32 +1400,33 @@ static void visitmsg(const upb_refcounted *r, upb_refcounted_visit *visit, static void freemsg(upb_refcounted *r) { upb_msgdef *m = (upb_msgdef*)r; - upb_strtable_uninit(&m->ntoo); upb_strtable_uninit(&m->ntof); upb_inttable_uninit(&m->itof); upb_def_uninit(upb_msgdef_upcast_mutable(m)); - free(m); + upb_gfree(m); } +const struct upb_refcounted_vtbl upb_msgdef_vtbl = {visitmsg, freemsg}; + upb_msgdef *upb_msgdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg}; - upb_msgdef *m = malloc(sizeof(*m)); + upb_msgdef *m = upb_gmalloc(sizeof(*m)); if (!m) return NULL; - if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner)) + + if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &upb_msgdef_vtbl, + owner)) { goto err2; - if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3; - if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2; - if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1; + } + + if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err1; m->map_entry = false; m->syntax = UPB_SYNTAX_PROTO2; return m; err1: - upb_strtable_uninit(&m->ntof); -err2: upb_inttable_uninit(&m->itof); -err3: - free(m); +err2: + upb_gfree(m); return NULL; } @@ -1415,6 +1484,19 @@ bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, return upb_def_setfullname(upb_msgdef_upcast_mutable(m), fullname, s); } +bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax) { + if (syntax != UPB_SYNTAX_PROTO2 && syntax != UPB_SYNTAX_PROTO3) { + return false; + } + + m->syntax = syntax; + return true; +} + +upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m) { + return m->syntax; +} + /* Helper: check that the field |f| is safe to add to msgdef |m|. Set an error * on status |s| and return false if not. */ static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, @@ -1425,9 +1507,11 @@ static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { upb_status_seterrmsg(s, "field name or number were not set"); return false; - } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) || - upb_msgdef_itof(m, upb_fielddef_number(f))) { - upb_status_seterrmsg(s, "duplicate field name or number for field"); + } else if (upb_msgdef_itof(m, upb_fielddef_number(f))) { + upb_status_seterrmsg(s, "duplicate field number"); + return false; + } else if (upb_strtable_lookup(&m->ntof, upb_fielddef_name(f), NULL)) { + upb_status_seterrmsg(s, "name conflicts with existing field or oneof"); return false; } return true; @@ -1488,8 +1572,8 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, } else if (upb_oneofdef_name(o) == NULL) { upb_status_seterrmsg(s, "oneofdef name was not set"); return false; - } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) { - upb_status_seterrmsg(s, "duplicate oneof name"); + } else if (upb_strtable_lookup(&m->ntof, upb_oneofdef_name(o), NULL)) { + upb_status_seterrmsg(s, "name conflicts with existing field or oneof"); return false; } @@ -1506,7 +1590,7 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, /* Add oneof itself first. */ o->parent = m; - upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o)); + upb_strtable_insert(&m->ntof, upb_oneofdef_name(o), upb_value_ptr(o)); upb_ref2(o, m); upb_ref2(m, o); @@ -1530,23 +1614,47 @@ const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, size_t len) { upb_value val; - return upb_strtable_lookup2(&m->ntof, name, len, &val) ? - upb_value_getptr(val) : NULL; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return NULL; + } + + return upb_trygetfield(upb_value_getptr(val)); } const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, size_t len) { upb_value val; - return upb_strtable_lookup2(&m->ntoo, name, len, &val) ? - upb_value_getptr(val) : NULL; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return NULL; + } + + return upb_trygetoneof(upb_value_getptr(val)); +} + +bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len, + const upb_fielddef **f, const upb_oneofdef **o) { + upb_value val; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return false; + } + + *o = upb_trygetoneof(upb_value_getptr(val)); + *f = upb_trygetfield(upb_value_getptr(val)); + assert((*o != NULL) ^ (*f != NULL)); /* Exactly one of the two should be set. */ + return true; } int upb_msgdef_numfields(const upb_msgdef *m) { - return upb_strtable_count(&m->ntof); + /* The number table contains only fields. */ + return upb_inttable_count(&m->itof); } int upb_msgdef_numoneofs(const upb_msgdef *m) { - return upb_strtable_count(&m->ntoo); + /* The name table includes oneofs, and the number table does not. */ + return upb_strtable_count(&m->ntof) - upb_inttable_count(&m->itof); } void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) { @@ -1577,10 +1685,21 @@ void upb_msg_field_iter_setdone(upb_msg_field_iter *iter) { } void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) { - upb_strtable_begin(iter, &m->ntoo); + upb_strtable_begin(iter, &m->ntof); + /* We need to skip past any initial fields. */ + while (!upb_strtable_done(iter) && + !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))) { + upb_strtable_next(iter); + } } -void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); } +void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { + /* We need to skip past fields to return only oneofs. */ + do { + upb_strtable_next(iter); + } while (!upb_strtable_done(iter) && + !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))); +} bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) { return upb_strtable_done(iter); @@ -1613,26 +1732,36 @@ static void freeoneof(upb_refcounted *r) { upb_oneofdef *o = (upb_oneofdef*)r; upb_strtable_uninit(&o->ntof); upb_inttable_uninit(&o->itof); - free((void*)o->name); - free(o); + upb_gfree((void*)o->name); + upb_gfree(o); } +const struct upb_refcounted_vtbl upb_oneofdef_vtbl = {visitoneof, freeoneof}; + upb_oneofdef *upb_oneofdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof}; - upb_oneofdef *o = malloc(sizeof(*o)); + upb_oneofdef *o = upb_gmalloc(sizeof(*o)); + + if (!o) { + return NULL; + } + o->parent = NULL; - if (!o) return NULL; - if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &vtbl, owner)) - goto err2; o->name = NULL; + + if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &upb_oneofdef_vtbl, + owner)) { + goto err2; + } + if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2; if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1; + return o; err1: upb_inttable_uninit(&o->itof); err2: - free(o); + upb_gfree(o); return NULL; } @@ -1661,9 +1790,19 @@ bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s) { upb_status_seterrmsg(s, "oneof already added to a message"); return false; } - if (!upb_isident(name, strlen(name), true, s)) return false; - free((void*)o->name); - o->name = upb_strdup(name); + + if (!upb_isident(name, strlen(name), true, s)) { + return false; + } + + name = upb_gstrdup(name); + if (!name) { + upb_status_seterrmsg(s, "One of memory"); + return false; + } + + upb_gfree((void*)o->name); + o->name = name; return true; } @@ -1805,14 +1944,15 @@ static void freefiledef(upb_refcounted *r) { upb_inttable_uninit(&f->defs); upb_inttable_uninit(&f->deps); - free((void*)f->name); - free((void*)f->package); - free(f); + upb_gfree((void*)f->name); + upb_gfree((void*)f->package); + upb_gfree(f); } +const struct upb_refcounted_vtbl upb_filedef_vtbl = {visitfiledef, freefiledef}; + upb_filedef *upb_filedef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {visitfiledef, freefiledef}; - upb_filedef *f = malloc(sizeof(*f)); + upb_filedef *f = upb_gmalloc(sizeof(*f)); if (!f) { return NULL; @@ -1822,7 +1962,8 @@ upb_filedef *upb_filedef_new(const void *owner) { f->name = NULL; f->syntax = UPB_SYNTAX_PROTO2; - if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &vtbl, owner)) { + if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &upb_filedef_vtbl, + owner)) { goto err; } @@ -1841,7 +1982,7 @@ err2: upb_inttable_uninit(&f->defs); err: - free(f); + upb_gfree(f); return NULL; } @@ -1886,12 +2027,12 @@ const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i) { } bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) { - name = upb_strdup(name); + name = upb_gstrdup(name); if (!name) { - upb_status_seterrmsg(s, "Out of memory"); + upb_upberr_setoom(s); return false; } - free((void*)f->name); + upb_gfree((void*)f->name); f->name = name; return true; } @@ -1899,12 +2040,12 @@ bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) { bool upb_filedef_setpackage(upb_filedef *f, const char *package, upb_status *s) { if (!upb_isident(package, strlen(package), true, s)) return false; - package = upb_strdup(package); + package = upb_gstrdup(package); if (!package) { - upb_status_seterrmsg(s, "Out of memory"); + upb_upberr_setoom(s); return false; } - free((void*)f->package); + upb_gfree((void*)f->package); f->package = package; return true; } @@ -1953,7 +2094,7 @@ bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor, } return true; } else { - upb_status_seterrmsg(s, "Out of memory."); + upb_upberr_setoom(s); return false; } } @@ -1967,288 +2108,22 @@ bool upb_filedef_adddep(upb_filedef *f, const upb_filedef *dep) { return false; } } - - -#include -#include -#include - -typedef struct cleanup_ent { - upb_cleanup_func *cleanup; - void *ud; - struct cleanup_ent *next; -} cleanup_ent; - -static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); - -/* Default allocator **********************************************************/ - -/* Just use realloc, keeping all allocated blocks in a linked list to destroy at - * the end. */ - -typedef struct mem_block { - /* List is doubly-linked, because in cases where realloc() moves an existing - * block, we need to be able to remove the old pointer from the list - * efficiently. */ - struct mem_block *prev, *next; -#ifndef NDEBUG - size_t size; /* Doesn't include mem_block structure. */ -#endif -} mem_block; - -typedef struct { - mem_block *head; -} default_alloc_ud; - -static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { - default_alloc_ud *ud = _ud; - mem_block *from, *block; - void *ret; - UPB_UNUSED(oldsize); - - from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; - -#ifndef NDEBUG - if (from) { - assert(oldsize <= from->size); - } -#endif - - /* TODO(haberman): we probably need to provide even better alignment here, - * like 16-byte alignment of the returned data pointer. */ - block = realloc(from, size + sizeof(mem_block)); - if (!block) return NULL; - ret = (char*)block + sizeof(*block); - -#ifndef NDEBUG - block->size = size; -#endif - - if (from) { - if (block != from) { - /* The block was moved, so pointers in next and prev blocks must be - * updated to its new location. */ - if (block->next) block->next->prev = block; - if (block->prev) block->prev->next = block; - if (ud->head == from) ud->head = block; - } - } else { - /* Insert at head of linked list. */ - block->prev = NULL; - block->next = ud->head; - if (block->next) block->next->prev = block; - ud->head = block; - } - - return ret; -} - -static void default_alloc_cleanup(void *_ud) { - default_alloc_ud *ud = _ud; - mem_block *block = ud->head; - - while (block) { - void *to_free = block; - block = block->next; - free(to_free); - } -} - - -/* Standard error functions ***************************************************/ - -static bool default_err(void *ud, const upb_status *status) { - UPB_UNUSED(ud); - UPB_UNUSED(status); - return false; -} - -static bool write_err_to(void *ud, const upb_status *status) { - upb_status *copy_to = ud; - upb_status_copy(copy_to, status); - return false; -} - - -/* upb_env ********************************************************************/ - -void upb_env_init(upb_env *e) { - default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; - e->ok_ = true; - e->bytes_allocated = 0; - e->cleanup_head = NULL; - - ud->head = NULL; - - /* Set default functions. */ - upb_env_setallocfunc(e, default_alloc, ud); - upb_env_seterrorfunc(e, default_err, NULL); -} - -void upb_env_uninit(upb_env *e) { - cleanup_ent *ent = e->cleanup_head; - - while (ent) { - ent->cleanup(ent->ud); - ent = ent->next; - } - - /* Must do this after running cleanup functions, because this will delete - the memory we store our cleanup entries in! */ - if (e->alloc == default_alloc) { - default_alloc_cleanup(e->alloc_ud); - } -} - -UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, - void *ud) { - e->alloc = alloc; - e->alloc_ud = ud; -} - -UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, - void *ud) { - e->err = func; - e->err_ud = ud; -} - -void upb_env_reporterrorsto(upb_env *e, upb_status *status) { - e->err = write_err_to; - e->err_ud = status; -} - -bool upb_env_ok(const upb_env *e) { - return e->ok_; -} - -bool upb_env_reporterror(upb_env *e, const upb_status *status) { - e->ok_ = false; - return e->err(e->err_ud, status); -} - -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { - cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); - if (!ent) return false; - - ent->cleanup = func; - ent->ud = ud; - ent->next = e->cleanup_head; - e->cleanup_head = ent; - - return true; -} - -void *upb_env_malloc(upb_env *e, size_t size) { - e->bytes_allocated += size; - if (e->alloc == seeded_alloc) { - /* This is equivalent to the next branch, but allows inlining for a - * measurable perf benefit. */ - return seeded_alloc(e->alloc_ud, NULL, 0, size); - } else { - return e->alloc(e->alloc_ud, NULL, 0, size); - } -} - -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { - char *ret; - assert(oldsize <= size); - ret = e->alloc(e->alloc_ud, ptr, oldsize, size); - -#ifndef NDEBUG - /* Overwrite non-preserved memory to ensure callers are passing the oldsize - * that they truly require. */ - memset(ret + oldsize, 0xff, size - oldsize); -#endif - - return ret; -} - -size_t upb_env_bytesallocated(const upb_env *e) { - return e->bytes_allocated; -} - - -/* upb_seededalloc ************************************************************/ - -/* Be conservative and choose 16 in case anyone is using SSE. */ -static const size_t maxalign = 16; - -static size_t align_up(size_t size) { - return ((size + maxalign - 1) / maxalign) * maxalign; -} - -UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, - size_t size) { - upb_seededalloc *a = ud; - - size = align_up(size); - - assert(a->mem_limit >= a->mem_ptr); - - if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { - /* Fast path: we can satisfy from the initial allocation. */ - void *ret = a->mem_ptr; - a->mem_ptr += size; - return ret; - } else { - char *chptr = ptr; - /* Slow path: fallback to other allocator. */ - a->need_cleanup = true; - /* Is `ptr` part of the user-provided initial block? Don't pass it to the - * default allocator if so; otherwise, it may try to realloc() the block. */ - if (chptr >= a->mem_base && chptr < a->mem_limit) { - void *ret; - assert(chptr + oldsize <= a->mem_limit); - ret = a->alloc(a->alloc_ud, NULL, 0, size); - if (ret) memcpy(ret, ptr, oldsize); - return ret; - } else { - return a->alloc(a->alloc_ud, ptr, oldsize, size); - } - } -} - -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { - default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; - a->mem_base = mem; - a->mem_ptr = mem; - a->mem_limit = (char*)mem + len; - a->need_cleanup = false; - a->returned_allocfunc = false; - - ud->head = NULL; - - upb_seededalloc_setfallbackalloc(a, default_alloc, ud); -} - -void upb_seededalloc_uninit(upb_seededalloc *a) { - if (a->alloc == default_alloc && a->need_cleanup) { - default_alloc_cleanup(a->alloc_ud); - } -} - -UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, - upb_alloc_func *alloc, - void *ud) { - assert(!a->returned_allocfunc); - a->alloc = alloc; - a->alloc_ud = ud; -} - -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { - a->returned_allocfunc = true; - return seeded_alloc; -} /* ** TODO(haberman): it's unclear whether a lot of the consistency checks should ** assert() or return false. */ -#include #include +static void *upb_calloc(size_t size) { + void *mem = upb_gmalloc(size); + if (mem) { + memset(mem, 0, size); + } + return mem; +} /* Defined for the sole purpose of having a unique pointer value for * UPB_NO_CLOSURE. */ @@ -2268,8 +2143,8 @@ static void freehandlers(upb_refcounted *r) { upb_inttable_uninit(&h->cleanup_); upb_msgdef_unref(h->msg, h); - free(h->sub); - free(h); + upb_gfree(h->sub); + upb_gfree(h); } static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -2519,14 +2394,20 @@ upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { assert(upb_msgdef_isfrozen(md)); extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1); - h = calloc(sizeof(*h) + extra, 1); + h = upb_calloc(sizeof(*h) + extra); if (!h) return NULL; h->msg = md; upb_msgdef_ref(h->msg, h); upb_status_clear(&h->status_); - h->sub = calloc(md->submsg_field_count, sizeof(*h->sub)); - if (!h->sub) goto oom; + + if (md->submsg_field_count > 0) { + h->sub = upb_calloc(md->submsg_field_count * sizeof(*h->sub)); + if (!h->sub) goto oom; + } else { + h->sub = 0; + } + if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner)) goto oom; if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom; @@ -2941,7 +2822,6 @@ bool upb_byteshandler_setendstr(upb_byteshandler *h, #include -#include static void freeobj(upb_refcounted *o); @@ -3017,8 +2897,31 @@ void upb_unlock(); /* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some * code-paths that can normally never fail, like upb_refcounted_ref(). Since * we have no way to propagage out-of-memory errors back to the user, and since - * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */ -#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); } + * these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that + * immediately aborts on failure (avoiding the global allocator, which might + * inject failures). */ + +#include + +static void *upb_debugrefs_allocfunc(upb_alloc *alloc, void *ptr, + size_t oldsize, size_t size) { + UPB_UNUSED(alloc); + UPB_UNUSED(oldsize); + if (size == 0) { + free(ptr); + return NULL; + } else { + void *ret = realloc(ptr, size); + + if (!ret) { + abort(); + } + + return ret; + } +} + +upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc}; typedef struct { int count; /* How many refs there are (duplicates only allowed for ref2). */ @@ -3026,8 +2929,7 @@ typedef struct { } trackedref; static trackedref *trackedref_new(bool is_ref2) { - trackedref *ret = malloc(sizeof(*ret)); - CHECK_OOM(ret); + trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret)); ret->count = 1; ret->is_ref2 = is_ref2; return ret; @@ -3052,15 +2954,15 @@ static void track(const upb_refcounted *r, const void *owner, bool ref2) { ref->count++; } else { trackedref *ref = trackedref_new(ref2); - bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref)); - CHECK_OOM(ok); + upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref), + &upb_alloc_debugrefs); if (ref2) { /* We know this cast is safe when it is a ref2, because it's coming from * another refcounted object. */ const upb_refcounted *from = owner; assert(!upb_inttable_lookupptr(from->ref2s, r, NULL)); - ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL)); - CHECK_OOM(ok); + upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL), + &upb_alloc_debugrefs); } } upb_unlock(); @@ -3118,7 +3020,6 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { upb_value v; upb_value count; trackedref *ref; - bool ok; bool found; upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); @@ -3129,8 +3030,7 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { ref = upb_value_getptr(v); count = upb_value_int32(ref->count); - ok = upb_inttable_insertptr(tab, to, count); - CHECK_OOM(ok); + upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs); } upb_unlock(); } @@ -3156,21 +3056,19 @@ static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, assert(removed); newcount = upb_value_getint32(v) - 1; if (newcount > 0) { - upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount)); + upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount), + &upb_alloc_debugrefs); } } static void visit(const upb_refcounted *r, upb_refcounted_visit *v, void *closure) { - bool ok; - /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know * exactly the set of nodes that visit() should visit. So we verify visit()'s * correctness here. */ check_state state; state.obj = r; - ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32); - CHECK_OOM(ok); + upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs); getref2s(r, &state.ref2); /* This should visit any children in the ref2 table. */ @@ -3178,32 +3076,22 @@ static void visit(const upb_refcounted *r, upb_refcounted_visit *v, /* This assertion will fail if the visit() function missed any children. */ assert(upb_inttable_count(&state.ref2) == 0); - upb_inttable_uninit(&state.ref2); + upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs); if (r->vtbl->visit) r->vtbl->visit(r, v, closure); } -static bool trackinit(upb_refcounted *r) { - r->refs = malloc(sizeof(*r->refs)); - r->ref2s = malloc(sizeof(*r->ref2s)); - if (!r->refs || !r->ref2s) goto err1; - - if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1; - if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2; - return true; - -err2: - upb_inttable_uninit(r->refs); -err1: - free(r->refs); - free(r->ref2s); - return false; +static void trackinit(upb_refcounted *r) { + r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs)); + r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s)); + upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs); + upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs); } static void trackfree(const upb_refcounted *r) { - upb_inttable_uninit(r->refs); - upb_inttable_uninit(r->ref2s); - free(r->refs); - free(r->ref2s); + upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs); + upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs); + upb_free(&upb_alloc_debugrefs, r->refs); + upb_free(&upb_alloc_debugrefs, r->ref2s); } #else @@ -3226,9 +3114,8 @@ static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { UPB_UNUSED(ref2); } -static bool trackinit(upb_refcounted *r) { +static void trackinit(upb_refcounted *r) { UPB_UNUSED(r); - return true; } static void trackfree(const upb_refcounted *r) { @@ -3338,12 +3225,12 @@ static upb_refcounted *pop(tarjan *t) { } static void tarjan_newgroup(tarjan *t) { - uint32_t *group = malloc(sizeof(*group)); + uint32_t *group = upb_gmalloc(sizeof(*group)); if (!group) oom(t); /* Push group and empty group leader (we'll fill in leader later). */ if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) || !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) { - free(group); + upb_gfree(group); oom(t); } *group = 0; @@ -3518,7 +3405,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, if (obj == move) { /* Removing the last object from a group. */ assert(*obj->group == obj->individual_count); - free(obj->group); + upb_gfree(obj->group); } else { obj->next = move->next; /* This may decrease to zero; we'll collect GRAY objects (if any) that @@ -3574,7 +3461,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, /* We eagerly free() the group's count (since we can't easily determine * the group's remaining size it's the easiest way to ensure it gets * done). */ - free(obj->group); + upb_gfree(obj->group); /* Visit to release ref2's (done in a separate pass since release_ref2 * depends on o->group being unmodified so it can test merged()). */ @@ -3594,7 +3481,7 @@ err4: if (!ret) { upb_inttable_begin(&iter, &t.groups); for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) - free(upb_value_getptr(upb_inttable_iter_value(&iter))); + upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter))); } upb_inttable_uninit(&t.groups); err3: @@ -3618,7 +3505,7 @@ static void merge(upb_refcounted *r, upb_refcounted *from) { if (merged(r, from)) return; *r->group += *from->group; - free(from->group); + upb_gfree(from->group); base = from; /* Set all refcount pointers in the "from" chain to the merged refcount. @@ -3652,7 +3539,7 @@ static void unref(const upb_refcounted *r) { if (unrefgroup(r->group)) { const upb_refcounted *o; - free(r->group); + upb_gfree(r->group); /* In two passes, since release_ref2 needs a guarantee that any subobjs * are alive. */ @@ -3696,13 +3583,10 @@ bool upb_refcounted_init(upb_refcounted *r, r->vtbl = vtbl; r->individual_count = 0; r->is_frozen = false; - r->group = malloc(sizeof(*r->group)); + r->group = upb_gmalloc(sizeof(*r->group)); if (!r->group) return false; *r->group = 0; - if (!trackinit(r)) { - free(r->group); - return false; - } + trackinit(r); upb_refcounted_ref(r, owner); return true; } @@ -3771,8 +3655,6 @@ bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, } -#include - /* Fallback implementation if the shim is not specialized by the JIT. */ #define SHIM_WRITER(type, ctype) \ bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \ @@ -3798,14 +3680,14 @@ bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; bool ok; - upb_shim_data *d = malloc(sizeof(*d)); + upb_shim_data *d = upb_gmalloc(sizeof(*d)); if (!d) return false; d->offset = offset; d->hasbit = hasbit; upb_handlerattr_sethandlerdata(&attr, d); upb_handlerattr_setalwaysok(&attr, true); - upb_handlers_addcleanup(h, d, free); + upb_handlers_addcleanup(h, d, upb_gfree); #define TYPE(u, l) \ case UPB_TYPE_##u: \ @@ -3856,7 +3738,6 @@ const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, } -#include #include static void upb_symtab_free(upb_refcounted *r) { @@ -3868,13 +3749,17 @@ static void upb_symtab_free(upb_refcounted *r) { upb_def_unref(def, s); } upb_strtable_uninit(&s->symtab); - free(s); + upb_gfree(s); } - upb_symtab *upb_symtab_new(const void *owner) { static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free}; - upb_symtab *s = malloc(sizeof(*s)); + + upb_symtab *s = upb_gmalloc(sizeof(*s)); + if (!s) { + return NULL; + } + upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner); upb_strtable_init(&s->symtab, UPB_CTYPE_PTR); return s; @@ -4061,6 +3946,10 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, upb_strtable addtab; upb_inttable seen; + if (n == 0 && !freeze_also) { + return true; + } + assert(!upb_symtab_isfrozen(s)); if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { upb_status_seterrmsg(status, "out of memory"); @@ -4209,7 +4098,7 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, add_objs_size++; } - add_defs = malloc(sizeof(void*) * add_objs_size); + add_defs = upb_gmalloc(sizeof(void*) * add_objs_size); if (add_defs == NULL) goto oom_err; upb_strtable_begin(&iter, &addtab); for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { @@ -4254,7 +4143,7 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); UPB_ASSERT_VAR(success, success == true); } - free(add_objs); + upb_gfree(add_defs); return true; oom_err: @@ -4275,7 +4164,7 @@ err: { } } upb_strtable_uninit(&addtab); - free(add_objs); + upb_gfree(add_defs); assert(!upb_ok(status)); return false; } @@ -4292,7 +4181,7 @@ bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) { bool ret; n = upb_filedef_defcount(file); - defs = malloc(sizeof(*defs) * n); + defs = upb_gmalloc(sizeof(*defs) * n); if (defs == NULL) { upb_status_seterrmsg(status, "Out of memory"); @@ -4305,7 +4194,7 @@ bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) { ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status); - free(defs); + upb_gfree(defs); return ret; } @@ -4347,7 +4236,6 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { */ -#include #include #define UPB_MAXARRSIZE 16 /* 64k. */ @@ -4356,6 +4244,17 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { #define ARRAY_SIZE(x) \ ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) +#ifdef NDEBUG +static void upb_check_alloc(upb_table *t, upb_alloc *a) { + UPB_UNUSED(t); + UPB_UNUSED(a); +} +#else +static void upb_check_alloc(upb_table *t, upb_alloc *a) { + assert(t->alloc == a); +} +#endif + static const double MAX_LOAD = 0.85; /* The minimum utilization of the array part of a mixed hash/array table. This @@ -4373,11 +4272,11 @@ int log2ceil(uint64_t v) { return UPB_MIN(UPB_MAXARRSIZE, ret); } -char *upb_strdup(const char *s) { - return upb_strdup2(s, strlen(s)); +char *upb_strdup(const char *s, upb_alloc *a) { + return upb_strdup2(s, strlen(s), a); } -char *upb_strdup2(const char *s, size_t len) { +char *upb_strdup2(const char *s, size_t len, upb_alloc *a) { size_t n; char *p; @@ -4386,7 +4285,7 @@ char *upb_strdup2(const char *s, size_t len) { /* Always null-terminate, even if binary data; but don't rely on the input to * have a null-terminating byte since it may be a raw binary buffer. */ n = len + 1; - p = malloc(n); + p = upb_malloc(a, n); if (p) { memcpy(p, s, len); p[len] = 0; @@ -4434,16 +4333,20 @@ static bool isfull(upb_table *t) { } } -static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { +static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2, + upb_alloc *a) { size_t bytes; t->count = 0; t->ctype = ctype; t->size_lg2 = size_lg2; t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0; +#ifndef NDEBUG + t->alloc = a; +#endif bytes = upb_table_size(t) * sizeof(upb_tabent); if (bytes > 0) { - t->entries = malloc(bytes); + t->entries = upb_malloc(a, bytes); if (!t->entries) return false; memset(mutable_entries(t), 0, bytes); } else { @@ -4452,7 +4355,10 @@ static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { return true; } -static void uninit(upb_table *t) { free(mutable_entries(t)); } +static void uninit(upb_table *t, upb_alloc *a) { + upb_check_alloc(t, a); + upb_free(a, mutable_entries(t)); +} static upb_tabent *emptyent(upb_table *t) { upb_tabent *e = mutable_entries(t) + upb_table_size(t); @@ -4605,8 +4511,8 @@ static size_t begin(const upb_table *t) { /* A simple "subclass" of upb_table that only adds a hash function for strings. */ -static upb_tabkey strcopy(lookupkey_t k2) { - char *str = malloc(k2.str.len + sizeof(uint32_t) + 1); +static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) { + char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1); if (str == NULL) return 0; memcpy(str, &k2.str.len, sizeof(uint32_t)); memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1); @@ -4625,51 +4531,56 @@ static bool streql(upb_tabkey k1, lookupkey_t k2) { return len == k2.str.len && memcmp(str, k2.str.str, len) == 0; } -bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { - return init(&t->t, ctype, 2); +bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) { + return init(&t->t, ctype, 2, a); } -void upb_strtable_uninit(upb_strtable *t) { +void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) { size_t i; for (i = 0; i < upb_table_size(&t->t); i++) - free((void*)t->t.entries[i].key); - uninit(&t->t); + upb_free(a, (void*)t->t.entries[i].key); + uninit(&t->t, a); } -bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) { upb_strtable new_table; upb_strtable_iter i; - if (!init(&new_table.t, t->t.ctype, size_lg2)) + upb_check_alloc(&t->t, a); + + if (!init(&new_table.t, t->t.ctype, size_lg2, a)) return false; upb_strtable_begin(&i, t); for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_strtable_insert2( + upb_strtable_insert3( &new_table, upb_strtable_iter_key(&i), upb_strtable_iter_keylength(&i), - upb_strtable_iter_value(&i)); + upb_strtable_iter_value(&i), + a); } - upb_strtable_uninit(t); + upb_strtable_uninit2(t, a); *t = new_table; return true; } -bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len, - upb_value v) { +bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len, + upb_value v, upb_alloc *a) { lookupkey_t key; upb_tabkey tabkey; uint32_t hash; + upb_check_alloc(&t->t, a); + if (isfull(&t->t)) { /* Need to resize. New table of double the size, add old elements to it. */ - if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) { + if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) { return false; } } key = strkey2(k, len); - tabkey = strcopy(key); + tabkey = strcopy(key, a); if (tabkey == 0) return false; hash = MurmurHash2(key.str.str, key.str.len, 0); @@ -4683,12 +4594,12 @@ bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, return lookup(&t->t, strkey2(key, len), v, hash, &streql); } -bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, - upb_value *val) { +bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len, + upb_value *val, upb_alloc *alloc) { uint32_t hash = MurmurHash2(key, strlen(key), 0); upb_tabkey tabkey; if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) { - free((void*)tabkey); + upb_free(alloc, (void*)tabkey); return true; } else { return false; @@ -4715,12 +4626,12 @@ bool upb_strtable_done(const upb_strtable_iter *i) { upb_tabent_isempty(str_tabent(i)); } -const char *upb_strtable_iter_key(upb_strtable_iter *i) { +const char *upb_strtable_iter_key(const upb_strtable_iter *i) { assert(!upb_strtable_done(i)); return upb_tabstr(str_tabent(i)->key, NULL); } -size_t upb_strtable_iter_keylength(upb_strtable_iter *i) { +size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) { uint32_t len; assert(!upb_strtable_done(i)); upb_tabstr(str_tabent(i)->key, &len); @@ -4795,18 +4706,18 @@ static void check(upb_inttable *t) { } bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, - size_t asize, int hsize_lg2) { + size_t asize, int hsize_lg2, upb_alloc *a) { size_t array_bytes; - if (!init(&t->t, ctype, hsize_lg2)) return false; + if (!init(&t->t, ctype, hsize_lg2, a)) return false; /* Always make the array part at least 1 long, so that we know key 0 * won't be in the hash part, which simplifies things. */ t->array_size = UPB_MAX(1, asize); t->array_count = 0; array_bytes = t->array_size * sizeof(upb_value); - t->array = malloc(array_bytes); + t->array = upb_malloc(a, array_bytes); if (!t->array) { - uninit(&t->t); + uninit(&t->t, a); return false; } memset(mutable_array(t), 0xff, array_bytes); @@ -4814,22 +4725,23 @@ bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, return true; } -bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) { - return upb_inttable_sizedinit(t, ctype, 0, 4); +bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) { + return upb_inttable_sizedinit(t, ctype, 0, 4, a); } -void upb_inttable_uninit(upb_inttable *t) { - uninit(&t->t); - free(mutable_array(t)); +void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) { + uninit(&t->t, a); + upb_free(a, mutable_array(t)); } -bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { - /* XXX: Table can't store value (uint64_t)-1. Need to somehow statically - * guarantee that this is not necessary, or fix the limitation. */ +bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val, + upb_alloc *a) { upb_tabval tabval; tabval.val = val.val; UPB_UNUSED(tabval); - assert(upb_arrhas(tabval)); + assert(upb_arrhas(tabval)); /* This will reject (uint64_t)-1. Fix this. */ + + upb_check_alloc(&t->t, a); if (key < t->array_size) { assert(!upb_arrhas(t->array[key])); @@ -4840,8 +4752,11 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { /* Need to resize the hash part, but we re-use the array part. */ size_t i; upb_table new_table; - if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1)) + + if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) { return false; + } + for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) { const upb_tabent *e = &t->t.entries[i]; uint32_t hash; @@ -4854,7 +4769,7 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { assert(t->t.count == new_table.count); - uninit(&t->t); + uninit(&t->t, a); t->t = new_table; } insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql); @@ -4900,8 +4815,9 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) { return success; } -bool upb_inttable_push(upb_inttable *t, upb_value val) { - return upb_inttable_insert(t, upb_inttable_count(t), val); +bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) { + upb_check_alloc(&t->t, a); + return upb_inttable_insert2(t, upb_inttable_count(t), val, a); } upb_value upb_inttable_pop(upb_inttable *t) { @@ -4911,8 +4827,10 @@ upb_value upb_inttable_pop(upb_inttable *t) { return val; } -bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) { - return upb_inttable_insert(t, (uintptr_t)key, val); +bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val, + upb_alloc *a) { + upb_check_alloc(&t->t, a); + return upb_inttable_insert2(t, (uintptr_t)key, val, a); } bool upb_inttable_lookupptr(const upb_inttable *t, const void *key, @@ -4924,7 +4842,7 @@ bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) { return upb_inttable_remove(t, (uintptr_t)key, val); } -void upb_inttable_compact(upb_inttable *t) { +void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) { /* A power-of-two histogram of the table keys. */ size_t counts[UPB_MAXARRSIZE + 1] = {0}; @@ -4936,6 +4854,8 @@ void upb_inttable_compact(upb_inttable *t) { int size_lg2; upb_inttable new_t; + upb_check_alloc(&t->t, a); + upb_inttable_begin(&i, t); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { uintptr_t key = upb_inttable_iter_key(&i); @@ -4968,16 +4888,16 @@ void upb_inttable_compact(upb_inttable *t) { size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0; size_t hashsize_lg2 = log2ceil(hash_size); - upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2); + upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a); upb_inttable_begin(&i, t); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { uintptr_t k = upb_inttable_iter_key(&i); - upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i)); + upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a); } assert(new_t.array_size == arr_size); assert(new_t.t.size_lg2 == hashsize_lg2); } - upb_inttable_uninit(t); + upb_inttable_uninit2(t, a); *t = new_t; } @@ -5253,6 +5173,19 @@ static void nullz(upb_status *status) { memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len); } + +/* upb_upberr *****************************************************************/ + +upb_errorspace upb_upberr = {"upb error"}; + +void upb_upberr_setoom(upb_status *status) { + status->error_space_ = &upb_upberr; + upb_status_seterrmsg(status, "Out of memory"); +} + + +/* upb_status *****************************************************************/ + void upb_status_clear(upb_status *status) { if (!status) return; status->ok_ = true; @@ -5291,20 +5224,258 @@ void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) { nullz(status); } -void upb_status_seterrcode(upb_status *status, upb_errorspace *space, - int code) { - if (!status) return; - status->ok_ = false; - status->error_space_ = space; - status->code_ = code; - space->set_message(status, code); -} - void upb_status_copy(upb_status *to, const upb_status *from) { if (!to) return; *to = *from; } -/* This file was generated by upbc (the upb compiler). + + +/* upb_alloc ******************************************************************/ + +static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + UPB_UNUSED(alloc); + UPB_UNUSED(oldsize); + if (size == 0) { + free(ptr); + return NULL; + } else { + return realloc(ptr, size); + } +} + +upb_alloc upb_alloc_global = {&upb_global_allocfunc}; + + +/* upb_arena ******************************************************************/ + +/* Be conservative and choose 16 in case anyone is using SSE. */ +static const size_t maxalign = 16; + +static size_t align_up(size_t size) { + return ((size + maxalign - 1) / maxalign) * maxalign; +} + +typedef struct mem_block { + struct mem_block *next; + size_t size; + size_t used; + bool owned; + /* Data follows. */ +} mem_block; + +typedef struct cleanup_ent { + struct cleanup_ent *next; + upb_cleanup_func *cleanup; + void *ud; +} cleanup_ent; + +static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size, + bool owned) { + mem_block *block = ptr; + + block->next = a->block_head; + block->size = size; + block->used = align_up(sizeof(mem_block)); + block->owned = owned; + + a->block_head = block; + + /* TODO(haberman): ASAN poison. */ +} + + +static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) { + size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block); + mem_block *block = upb_malloc(a->block_alloc, block_size); + + if (!block) { + return NULL; + } + + upb_arena_addblock(a, block, block_size, true); + a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size); + + return block; +} + +static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + upb_arena *a = (upb_arena*)alloc; /* upb_alloc is initial member. */ + mem_block *block = a->block_head; + void *ret; + + if (size == 0) { + return NULL; /* We are an arena, don't need individual frees. */ + } + + size = align_up(size); + + /* TODO(haberman): special-case if this is a realloc of the last alloc? */ + + if (!block || block->size - block->used < size) { + /* Slow path: have to allocate a new block. */ + block = upb_arena_allocblock(a, size); + + if (!block) { + return NULL; /* Out of memory. */ + } + } + + ret = (char*)block + block->used; + block->used += size; + + if (oldsize > 0) { + memcpy(ret, ptr, oldsize); /* Preserve existing data. */ + } + + /* TODO(haberman): ASAN unpoison. */ + + a->bytes_allocated += size; + return ret; +} + +/* Public Arena API ***********************************************************/ + +void upb_arena_init(upb_arena *a) { + a->alloc.func = &upb_arena_doalloc; + a->block_alloc = &upb_alloc_global; + a->bytes_allocated = 0; + a->next_block_size = 256; + a->max_block_size = 16384; + a->cleanup_head = NULL; + a->block_head = NULL; +} + +void upb_arena_init2(upb_arena *a, void *mem, size_t size, upb_alloc *alloc) { + upb_arena_init(a); + + if (size > sizeof(mem_block)) { + upb_arena_addblock(a, mem, size, false); + } + + if (alloc) { + a->block_alloc = alloc; + } +} + +void upb_arena_uninit(upb_arena *a) { + cleanup_ent *ent = a->cleanup_head; + mem_block *block = a->block_head; + + while (ent) { + ent->cleanup(ent->ud); + ent = ent->next; + } + + /* Must do this after running cleanup functions, because this will delete + * the memory we store our cleanup entries in! */ + while (block) { + mem_block *next = block->next; + + if (block->owned) { + upb_free(a->block_alloc, block); + } + + block = next; + } +} + +bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) { + cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent)); + if (!ent) { + return false; /* Out of memory. */ + } + + ent->cleanup = func; + ent->ud = ud; + ent->next = a->cleanup_head; + a->cleanup_head = ent; + + return true; +} + +size_t upb_arena_bytesallocated(const upb_arena *a) { + return a->bytes_allocated; +} + + +/* Standard error functions ***************************************************/ + +static bool default_err(void *ud, const upb_status *status) { + UPB_UNUSED(ud); + UPB_UNUSED(status); + return false; +} + +static bool write_err_to(void *ud, const upb_status *status) { + upb_status *copy_to = ud; + upb_status_copy(copy_to, status); + return false; +} + + +/* upb_env ********************************************************************/ + +void upb_env_initonly(upb_env *e) { + e->ok_ = true; + e->error_func_ = &default_err; + e->error_ud_ = NULL; +} + +void upb_env_init(upb_env *e) { + upb_arena_init(&e->arena_); + upb_env_initonly(e); +} + +void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc) { + upb_arena_init2(&e->arena_, mem, n, alloc); + upb_env_initonly(e); +} + +void upb_env_uninit(upb_env *e) { + upb_arena_uninit(&e->arena_); +} + +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud) { + e->error_func_ = func; + e->error_ud_ = ud; +} + +void upb_env_reporterrorsto(upb_env *e, upb_status *s) { + e->error_func_ = &write_err_to; + e->error_ud_ = s; +} + +bool upb_env_reporterror(upb_env *e, const upb_status *status) { + e->ok_ = false; + return e->error_func_(e->error_ud_, status); +} + +void *upb_env_malloc(upb_env *e, size_t size) { + return upb_malloc(&e->arena_.alloc, size); +} + +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + return upb_realloc(&e->arena_.alloc, ptr, oldsize, size); +} + +void upb_env_free(upb_env *e, void *ptr) { + upb_free(&e->arena_.alloc, ptr); +} + +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { + return upb_arena_addcleanup(&e->arena_, func, ud); +} + +size_t upb_env_bytesallocated(const upb_env *e) { + return upb_arena_bytesallocated(&e->arena_); +} +/* This file was generated by upbc (the upb compiler) from the input + * file: + * + * upb/descriptor/descriptor.proto + * * Do not edit -- your changes will be discarded when the file is * regenerated. */ @@ -5323,28 +5494,28 @@ static upb_inttable reftables[264]; #endif static const upb_msgdef msgs[22] = { - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]),&reftables[0], &reftables[1]), - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]),&reftables[2], &reftables[3]), - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]),&reftables[4], &reftables[5]), - UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]),&reftables[6], &reftables[7]), - UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]),&reftables[8], &reftables[9]), - UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]),&reftables[10], &reftables[11]), - UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]),&reftables[12], &reftables[13]), - UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]),&reftables[14], &reftables[15]), - UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]),&reftables[16], &reftables[17]), - UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]),&reftables[18], &reftables[19]), - UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]),&reftables[20], &reftables[21]), - UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]),&reftables[22], &reftables[23]), - UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]),&reftables[24], &reftables[25]), - UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]),&reftables[26], &reftables[27]), - UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]),&reftables[28], &reftables[29]), - UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]),&reftables[30], &reftables[31]), - UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]),&reftables[32], &reftables[33]), - UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]),&reftables[34], &reftables[35]), - UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]),&reftables[36], &reftables[37]), - UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]),&reftables[38], &reftables[39]), - UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]),&reftables[40], &reftables[41]), - UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]),&reftables[42], &reftables[43]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]), false, UPB_SYNTAX_PROTO2, &reftables[0], &reftables[1]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]), false, UPB_SYNTAX_PROTO2, &reftables[2], &reftables[3]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]), false, UPB_SYNTAX_PROTO2, &reftables[4], &reftables[5]), + UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]), false, UPB_SYNTAX_PROTO2, &reftables[6], &reftables[7]), + UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]), false, UPB_SYNTAX_PROTO2, &reftables[8], &reftables[9]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]), false, UPB_SYNTAX_PROTO2, &reftables[10], &reftables[11]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]), false, UPB_SYNTAX_PROTO2, &reftables[12], &reftables[13]), + UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]), false, UPB_SYNTAX_PROTO2, &reftables[14], &reftables[15]), + UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]), false, UPB_SYNTAX_PROTO2, &reftables[16], &reftables[17]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]), false, UPB_SYNTAX_PROTO2, &reftables[18], &reftables[19]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]), false, UPB_SYNTAX_PROTO2, &reftables[20], &reftables[21]), + UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]), false, UPB_SYNTAX_PROTO2, &reftables[22], &reftables[23]), + UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]), false, UPB_SYNTAX_PROTO2, &reftables[24], &reftables[25]), + UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]), false, UPB_SYNTAX_PROTO2, &reftables[26], &reftables[27]), + UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]), false, UPB_SYNTAX_PROTO2, &reftables[28], &reftables[29]), + UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]), false, UPB_SYNTAX_PROTO2, &reftables[30], &reftables[31]), + UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]), false, UPB_SYNTAX_PROTO2, &reftables[32], &reftables[33]), + UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]), false, UPB_SYNTAX_PROTO2, &reftables[34], &reftables[35]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]), false, UPB_SYNTAX_PROTO2, &reftables[36], &reftables[37]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]), false, UPB_SYNTAX_PROTO2, &reftables[38], &reftables[39]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]), false, UPB_SYNTAX_PROTO2, &reftables[40], &reftables[41]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]), false, UPB_SYNTAX_PROTO2, &reftables[42], &reftables[43]), }; static const upb_fielddef fields[105] = { @@ -6277,7 +6448,7 @@ struct upb_descreader { }; static char *upb_strndup(const char *buf, size_t n) { - char *ret = malloc(n + 1); + char *ret = upb_gmalloc(n + 1); if (!ret) return NULL; memcpy(ret, buf, n); ret[n] = '\0'; @@ -6291,9 +6462,12 @@ static char *upb_strndup(const char *buf, size_t n) { * Caller owns a ref on the returned string. */ static char *upb_join(const char *base, const char *name) { if (!base || strlen(base) == 0) { - return upb_strdup(name); + return upb_gstrdup(name); } else { - char *ret = malloc(strlen(base) + strlen(name) + 2); + char *ret = upb_gmalloc(strlen(base) + strlen(name) + 2); + if (!ret) { + return NULL; + } ret[0] = '\0'; strcat(ret, base); strcat(ret, "."); @@ -6303,14 +6477,20 @@ static char *upb_join(const char *base, const char *name) { } /* Qualify the defname for all defs starting with offset "start" with "str". */ -static void upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) { +static bool upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) { size_t i; for (i = start; i < upb_filedef_defcount(f); i++) { upb_def *def = upb_filedef_mutabledef(f, i); char *name = upb_join(str, upb_def_fullname(def)); + if (!name) { + /* Need better logic here; at this point we've qualified some names but + * not others. */ + return false; + } upb_def_setfullname(def, name, NULL); - free(name); + upb_gfree(name); } + return true; } @@ -6336,16 +6516,19 @@ void upb_descreader_startcontainer(upb_descreader *r) { f->name = NULL; } -void upb_descreader_endcontainer(upb_descreader *r) { +bool upb_descreader_endcontainer(upb_descreader *r) { upb_descreader_frame *f = &r->stack[--r->stack_len]; - upb_descreader_qualify(r->file, f->name, f->start); - free(f->name); + if (!upb_descreader_qualify(r->file, f->name, f->start)) { + return false; + } + upb_gfree(f->name); f->name = NULL; + return true; } void upb_descreader_setscopename(upb_descreader *r, char *str) { upb_descreader_frame *f = &r->stack[r->stack_len-1]; - free(f->name); + upb_gfree(f->name); f->name = str; } @@ -6372,8 +6555,7 @@ static bool file_end(void *closure, const void *hd, upb_status *status) { upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(status); - upb_descreader_endcontainer(r); - return true; + return upb_descreader_endcontainer(r); } static size_t file_onname(void *closure, const void *hd, const char *buf, @@ -6387,6 +6569,7 @@ static size_t file_onname(void *closure, const void *hd, const char *buf, name = upb_strndup(buf, n); /* XXX: see comment at the top of the file. */ ok = upb_filedef_setname(r->file, name, NULL); + upb_gfree(name); UPB_ASSERT_VAR(ok, ok); return n; } @@ -6470,7 +6653,7 @@ static size_t enumval_onname(void *closure, const void *hd, const char *buf, UPB_UNUSED(hd); UPB_UNUSED(handle); /* XXX: see comment at the top of the file. */ - free(r->name); + upb_gfree(r->name); r->name = upb_strndup(buf, n); r->saw_name = true; return n; @@ -6495,7 +6678,7 @@ static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) { } e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); upb_enumdef_addval(e, r->name, r->number, status); - free(r->name); + upb_gfree(r->name); r->name = NULL; return true; } @@ -6527,7 +6710,7 @@ static size_t enum_onname(void *closure, const void *hd, const char *buf, UPB_UNUSED(handle); /* XXX: see comment at the top of the file. */ upb_def_setfullname(upb_descreader_last(r), fullname, NULL); - free(fullname); + upb_gfree(fullname); return n; } @@ -6537,7 +6720,7 @@ static bool field_startmsg(void *closure, const void *hd) { upb_descreader *r = closure; UPB_UNUSED(hd); assert(r->f); - free(r->default_string); + upb_gfree(r->default_string); r->default_string = NULL; /* fielddefs default to packed, but descriptors default to non-packed. */ @@ -6696,7 +6879,7 @@ static size_t field_onname(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setname(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6709,7 +6892,7 @@ static size_t field_ontypename(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setsubdefname(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6722,7 +6905,7 @@ static size_t field_onextendee(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setcontainingtypename(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6735,7 +6918,7 @@ static size_t field_ondefaultval(void *closure, const void *hd, const char *buf, /* Have to convert from string to the correct type, but we might not know the * type yet, so we save it as a string until the end of the field. * XXX: see comment at the top of the file. */ - free(r->default_string); + upb_gfree(r->default_string); r->default_string = upb_strndup(buf, n); return n; } @@ -6759,8 +6942,7 @@ static bool msg_end(void *closure, const void *hd, upb_status *status) { upb_status_seterrmsg(status, "Encountered message with no name."); return false; } - upb_descreader_endcontainer(r); - return true; + return upb_descreader_endcontainer(r); } static size_t msg_name(void *closure, const void *hd, const char *buf, @@ -6814,6 +6996,17 @@ static bool msg_endfield(void *closure, const void *hd) { return true; } +static bool msg_onmapentry(void *closure, const void *hd, bool mapentry) { + upb_descreader *r = closure; + upb_msgdef *m = upb_descreader_top(r); + UPB_UNUSED(hd); + + upb_msgdef_setmapentry(m, mapentry); + r->f = NULL; + return true; +} + + /** Code to register handlers *************************************************/ @@ -6884,6 +7077,8 @@ static void reghandlers(const void *closure, upb_handlers *h) { } else if (upbdefs_google_protobuf_FieldOptions_is(m)) { upb_handlers_setbool(h, F(FieldOptions, lazy), &field_onlazy, NULL); upb_handlers_setbool(h, F(FieldOptions, packed), &field_onpacked, NULL); + } else if (upbdefs_google_protobuf_MessageOptions_is(m)) { + upb_handlers_setbool(h, F(MessageOptions, map_entry), &msg_onmapentry, NULL); } assert(upb_ok(upb_handlers_status(h))); @@ -6899,12 +7094,12 @@ void descreader_cleanup(void *_r) { upb_filedef_unref(upb_descreader_file(r, i), &r->files); } - free(r->name); + upb_gfree(r->name); upb_inttable_uninit(&r->files); - free(r->default_string); + upb_gfree(r->default_string); while (r->stack_len > 0) { upb_descreader_frame *f = &r->stack[--r->stack_len]; - free(f->name); + upb_gfree(f->name); } } @@ -6980,8 +7175,8 @@ static void freegroup(upb_refcounted *r) { #ifdef UPB_USE_JIT_X64 upb_pbdecoder_freejit(g); #endif - free(g->bytecode); - free(g); + upb_gfree(g->bytecode); + upb_gfree(g); } static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -6996,7 +7191,7 @@ static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, } mgroup *newgroup(const void *owner) { - mgroup *g = malloc(sizeof(*g)); + mgroup *g = upb_gmalloc(sizeof(*g)); static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup}; upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner); upb_inttable_init(&g->methods, UPB_CTYPE_PTR); @@ -7016,7 +7211,7 @@ static void freemethod(upb_refcounted *r) { } upb_inttable_uninit(&method->dispatch); - free(method); + upb_gfree(method); } static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -7028,7 +7223,7 @@ static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, mgroup *group) { static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod}; - upb_pbdecodermethod *ret = malloc(sizeof(*ret)); + upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret)); upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret); upb_byteshandler_init(&ret->input_handler_); @@ -7091,7 +7286,7 @@ typedef struct { } compiler; static compiler *newcompiler(mgroup *group, bool lazy) { - compiler *ret = malloc(sizeof(*ret)); + compiler *ret = upb_gmalloc(sizeof(*ret)); int i; ret->group = group; @@ -7104,7 +7299,7 @@ static compiler *newcompiler(mgroup *group, bool lazy) { } static void freecompiler(compiler *c) { - free(c); + upb_gfree(c); } const size_t ptr_words = sizeof(void*) / sizeof(uint32_t); @@ -7208,7 +7403,8 @@ static void put32(compiler *c, uint32_t v) { size_t oldsize = g->bytecode_end - g->bytecode; size_t newsize = UPB_MAX(oldsize * 2, 64); /* TODO(haberman): handle OOM. */ - g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t)); + g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t), + newsize * sizeof(uint32_t)); g->bytecode_end = g->bytecode + newsize; c->pc = g->bytecode + ofs; } @@ -9073,7 +9269,6 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { */ -#include /* The output buffer is divided into segments; a segment is a string of data * that is "ready to go" -- it does not need any varint lengths inserted into @@ -9318,12 +9513,12 @@ static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt, upb_handlerattr *attr) { uint32_t n = upb_fielddef_number(f); - tag_t *tag = malloc(sizeof(tag_t)); + tag_t *tag = upb_gmalloc(sizeof(tag_t)); tag->bytes = upb_vencode64((n << 3) | wt, tag->tag); upb_handlerattr_init(attr); upb_handlerattr_sethandlerdata(attr, tag); - upb_handlers_addcleanup(h, tag, free); + upb_handlers_addcleanup(h, tag, upb_gfree); } static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) { @@ -9582,9 +9777,6 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } -#include -#include -#include upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner, upb_status *status) { @@ -9615,7 +9807,7 @@ upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner, goto cleanup; } - ret = malloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1)); + ret = upb_gmalloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1)); if (!ret) { goto cleanup; @@ -9647,7 +9839,6 @@ cleanup: #include #include #include -#include #include @@ -9743,14 +9934,14 @@ bool putf(upb_textprinter *p, const char *fmt, ...) { va_end(args_copy); /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */ - str = malloc(len + 1); + str = upb_gmalloc(len + 1); if (!str) return false; written = vsprintf(str, fmt, args); va_end(args); UPB_ASSERT_VAR(written, written == len); ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); - free(str); + upb_gfree(str); return ok; } @@ -10119,12 +10310,11 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { ** - handling of keys/escape-sequences/etc that span input buffers. */ -#include -#include #include -#include -#include #include +#include +#include +#include #define UPB_JSON_MAX_DEPTH 64 @@ -11247,11 +11437,11 @@ static void end_object(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 1246 "upb/json/parser.rl" +#line 1245 "upb/json/parser.rl" -#line 1158 "upb/json/parser.c" +#line 1157 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 2, 1, 3, 1, 5, 1, 6, 1, 7, 1, 8, 1, @@ -11400,7 +11590,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1249 "upb/json/parser.rl" +#line 1248 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -11422,7 +11612,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 1329 "upb/json/parser.c" +#line 1328 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11497,118 +11687,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1161 "upb/json/parser.rl" +#line 1160 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1162 "upb/json/parser.rl" +#line 1161 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1166 "upb/json/parser.rl" +#line 1165 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1167 "upb/json/parser.rl" +#line 1166 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1173 "upb/json/parser.rl" +#line 1172 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1174 "upb/json/parser.rl" +#line 1173 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1175 "upb/json/parser.rl" +#line 1174 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1181 "upb/json/parser.rl" +#line 1180 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1187 "upb/json/parser.rl" +#line 1186 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1190 "upb/json/parser.rl" +#line 1189 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1192 "upb/json/parser.rl" +#line 1191 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1197 "upb/json/parser.rl" +#line 1196 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1198 "upb/json/parser.rl" +#line 1197 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1201 "upb/json/parser.rl" +#line 1200 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1207 "upb/json/parser.rl" +#line 1206 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1210 "upb/json/parser.rl" +#line 1209 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1216 "upb/json/parser.rl" +#line 1215 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1220 "upb/json/parser.rl" +#line 1219 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1225 "upb/json/parser.rl" +#line 1224 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1226 "upb/json/parser.rl" +#line 1225 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1228 "upb/json/parser.rl" +#line 1227 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1229 "upb/json/parser.rl" +#line 1228 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1231 "upb/json/parser.rl" +#line 1230 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1233 "upb/json/parser.rl" +#line 1232 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1235 "upb/json/parser.rl" +#line 1234 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1237 "upb/json/parser.rl" +#line 1236 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1238 "upb/json/parser.rl" +#line 1237 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1243 "upb/json/parser.rl" +#line 1242 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1515 "upb/json/parser.c" +#line 1514 "upb/json/parser.c" } } @@ -11621,7 +11811,7 @@ _again: _out: {} } -#line 1270 "upb/json/parser.rl" +#line 1269 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p); @@ -11662,13 +11852,13 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 1569 "upb/json/parser.c" +#line 1568 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1310 "upb/json/parser.rl" +#line 1309 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -11694,12 +11884,12 @@ static void free_json_parsermethod(upb_refcounted *r) { upb_value val = upb_inttable_iter_value(&i); upb_strtable *t = upb_value_getptr(val); upb_strtable_uninit(t); - free(t); + upb_gfree(t); } upb_inttable_uninit(&method->name_tables); - free(r); + upb_gfree(r); } static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { @@ -11716,7 +11906,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { } /* TODO(haberman): handle malloc failure. */ - t = malloc(sizeof(*t)); + t = upb_gmalloc(sizeof(*t)); upb_strtable_init(t, UPB_CTYPE_CONSTPTR); upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t)); @@ -11729,7 +11919,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { size_t field_len = upb_fielddef_getjsonname(f, buf, len); if (field_len > len) { size_t len2; - buf = realloc(buf, field_len); + buf = upb_grealloc(buf, 0, field_len); len = field_len; len2 = upb_fielddef_getjsonname(f, buf, len); UPB_ASSERT_VAR(len2, len == len2); @@ -11748,7 +11938,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { } } - free(buf); + upb_gfree(buf); } /* Public API *****************************************************************/ @@ -11788,7 +11978,7 @@ upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md, const void* owner) { static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod, free_json_parsermethod}; - upb_json_parsermethod *ret = malloc(sizeof(*ret)); + upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret)); upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner); ret->msg = md; @@ -11815,8 +12005,6 @@ const upb_byteshandler *upb_json_parsermethod_inputhandler( */ -#include -#include #include #include @@ -11849,22 +12037,22 @@ typedef struct { void freestrpc(void *ptr) { strpc *pc = ptr; - free(pc->ptr); - free(pc); + upb_gfree(pc->ptr); + upb_gfree(pc); } /* Convert fielddef name to JSON name and return as a string piece. */ strpc *newstrpc(upb_handlers *h, const upb_fielddef *f, bool preserve_fieldnames) { /* TODO(haberman): handle malloc failure. */ - strpc *ret = malloc(sizeof(*ret)); + strpc *ret = upb_gmalloc(sizeof(*ret)); if (preserve_fieldnames) { - ret->ptr = upb_strdup(upb_fielddef_name(f)); + ret->ptr = upb_gstrdup(upb_fielddef_name(f)); ret->len = strlen(ret->ptr); } else { size_t len; ret->len = upb_fielddef_getjsonname(f, NULL, 0); - ret->ptr = malloc(ret->len); + ret->ptr = upb_gmalloc(ret->len); len = upb_fielddef_getjsonname(f, ret->ptr, ret->len); UPB_ASSERT_VAR(len, len == ret->len); ret->len--; /* NULL */ @@ -12376,10 +12564,10 @@ static void set_enum_hd(upb_handlers *h, const upb_fielddef *f, bool preserve_fieldnames, upb_handlerattr *attr) { - EnumHandlerData *hd = malloc(sizeof(EnumHandlerData)); + EnumHandlerData *hd = upb_gmalloc(sizeof(EnumHandlerData)); hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f); hd->keyname = newstrpc(h, f, preserve_fieldnames); - upb_handlers_addcleanup(h, hd, free); + upb_handlers_addcleanup(h, hd, upb_gfree); upb_handlerattr_sethandlerdata(attr, hd); } diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index b00581618..2faf74e50 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -80,6 +80,18 @@ #include #include +#ifdef __cplusplus +namespace upb { +class Allocator; +class Arena; +class Environment; +class ErrorSpace; +class Status; +template class InlinedArena; +template class InlinedEnvironment; +} +#endif + /* UPB_INLINE: inline if possible, emit standalone code if required. */ #ifdef __cplusplus #define UPB_INLINE inline @@ -147,6 +159,7 @@ #define UPB_ASSERT_STDLAYOUT(type) \ static_assert(std::is_standard_layout::value, \ #type " must be standard layout"); +#define UPB_FINAL final #else /* !defined(UPB_CXX11) */ #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ class_name(const class_name&); \ @@ -156,6 +169,7 @@ ~class_name(); \ UPB_DISALLOW_COPY_AND_ASSIGN(class_name) #define UPB_ASSERT_STDLAYOUT(type) +#define UPB_FINAL #endif /* UPB_DECLARE_TYPE() @@ -257,6 +271,7 @@ /* Generic function type. */ typedef void upb_func(); + /* C++ Casts ******************************************************************/ #ifdef __cplusplus @@ -334,134 +349,18 @@ class PointerBase2 : public PointerBase { #endif -/* upb::reffed_ptr ************************************************************/ +/* upb::ErrorSpace ************************************************************/ -#ifdef __cplusplus - -#include /* For std::swap(). */ - -namespace upb { - -/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a - * ref on whatever object it points to (if any). */ -template class reffed_ptr { - public: - reffed_ptr() : ptr_(NULL) {} - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - reffed_ptr(U* val, const void* ref_donor = NULL) - : ptr_(upb::upcast(val)) { - if (ref_donor) { - assert(ptr_); - ptr_->DonateRef(ref_donor, this); - } else if (ptr_) { - ptr_->Ref(this); - } - } - - template - reffed_ptr(const reffed_ptr& other) - : ptr_(upb::upcast(other.get())) { - if (ptr_) ptr_->Ref(this); - } - - reffed_ptr(const reffed_ptr& other) - : ptr_(upb::upcast(other.get())) { - if (ptr_) ptr_->Ref(this); - } - - ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } - - template - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - /* TODO(haberman): add C++11 move construction/assignment for greater - * efficiency. */ - - void swap(reffed_ptr& other) { - if (ptr_ == other.ptr_) { - return; - } - - if (ptr_) ptr_->DonateRef(this, &other); - if (other.ptr_) other.ptr_->DonateRef(&other, this); - std::swap(ptr_, other.ptr_); - } - - T& operator*() const { - assert(ptr_); - return *ptr_; - } - - T* operator->() const { - assert(ptr_); - return ptr_; - } - - T* get() const { return ptr_; } - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - void reset(U* ptr = NULL, const void* ref_donor = NULL) { - reffed_ptr(ptr, ref_donor).swap(*this); - } - - template - reffed_ptr down_cast() { - return reffed_ptr(upb::down_cast(get())); - } - - template - reffed_ptr dyn_cast() { - return reffed_ptr(upb::dyn_cast(get())); - } - - /* Plain release() is unsafe; if we were the only owner, it would leak the - * object. Instead we provide this: */ - T* ReleaseTo(const void* new_owner) { - T* ret = NULL; - ptr_->DonateRef(this, new_owner); - std::swap(ret, ptr_); - return ret; - } - - private: - T* ptr_; -}; - -} /* namespace upb */ - -#endif /* __cplusplus */ - - -/* upb::Status ****************************************************************/ - -#ifdef __cplusplus -namespace upb { -class ErrorSpace; -class Status; -} -#endif +/* A upb::ErrorSpace represents some domain of possible error values. This lets + * upb::Status attach specific error codes to operations, like POSIX/C errno, + * Win32 error codes, etc. Clients who want to know the very specific error + * code can check the error space and then know the type of the integer code. + * + * NOTE: upb::ErrorSpace is currently not used and should be considered + * experimental. It is important primarily in cases where upb is performing + * I/O, but upb doesn't currently have any components that do this. */ UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace) -UPB_DECLARE_TYPE(upb::Status, upb_status) - -/* The maximum length of an error message before it will get truncated. */ -#define UPB_STATUS_MAX_MESSAGE 128 - -/* An error callback function is used to report errors from some component. - * The function can return "true" to indicate that the component should try - * to recover and proceed, but this is not always possible. */ -typedef bool upb_errcb_t(void *closure, const upb_status* status); #ifdef __cplusplus class upb::ErrorSpace { @@ -469,67 +368,21 @@ class upb::ErrorSpace { struct upb_errorspace { #endif const char *name; - /* Should the error message in the status object according to this code. */ - void (*set_message)(upb_status* status, int code); }; -#ifdef __cplusplus -/* Object representing a success or failure status. +/* upb::Status ****************************************************************/ + +/* upb::Status represents a success or failure status and error message. * It owns no resources and allocates no memory, so it should work * even in OOM situations. */ +UPB_DECLARE_TYPE(upb::Status, upb_status) -class upb::Status { - public: - Status(); +/* The maximum length of an error message before it will get truncated. */ +#define UPB_STATUS_MAX_MESSAGE 128 - /* Returns true if there is no error. */ - bool ok() const; +UPB_BEGIN_EXTERN_C - /* Optional error space and code, useful if the caller wants to - * programmatically check the specific kind of error. */ - ErrorSpace* error_space(); - int code() const; - - const char *error_message() const; - - /* The error message will be truncated if it is longer than - * UPB_STATUS_MAX_MESSAGE-4. */ - void SetErrorMessage(const char* msg); - void SetFormattedErrorMessage(const char* fmt, ...); - - /* If there is no error message already, this will use the ErrorSpace to - * populate the error message for this code. The caller can still call - * SetErrorMessage() to give a more specific message. */ - void SetErrorCode(ErrorSpace* space, int code); - - /* Resets the status to a successful state with no message. */ - void Clear(); - - void CopyFrom(const Status& other); - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Status) -#else -struct upb_status { -#endif - bool ok_; - - /* Specific status code defined by some error space (optional). */ - int code_; - upb_errorspace *error_space_; - - /* Error message; NULL-terminated. */ - char msg[UPB_STATUS_MAX_MESSAGE]; -}; - -#define UPB_STATUS_INIT {true, 0, NULL, {0}} - -#ifdef __cplusplus -extern "C" { -#endif - -/* The returned string is invalidated by any other call into the status. */ const char *upb_status_errmsg(const upb_status *status); bool upb_ok(const upb_status *status); upb_errorspace *upb_status_errspace(const upb_status *status); @@ -542,40 +395,384 @@ void upb_status_clear(upb_status *status); void upb_status_seterrmsg(upb_status *status, const char *msg); void upb_status_seterrf(upb_status *status, const char *fmt, ...); void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args); -void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code); void upb_status_copy(upb_status *to, const upb_status *from); +UPB_END_EXTERN_C + #ifdef __cplusplus -} /* extern "C" */ -namespace upb { +class upb::Status { + public: + Status() { upb_status_clear(this); } -/* C++ Wrappers */ -inline Status::Status() { Clear(); } -inline bool Status::ok() const { return upb_ok(this); } -inline const char* Status::error_message() const { - return upb_status_errmsg(this); -} -inline void Status::SetErrorMessage(const char* msg) { - upb_status_seterrmsg(this, msg); -} -inline void Status::SetFormattedErrorMessage(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - upb_status_vseterrf(this, fmt, args); - va_end(args); -} -inline void Status::SetErrorCode(ErrorSpace* space, int code) { - upb_status_seterrcode(this, space, code); -} -inline void Status::Clear() { upb_status_clear(this); } -inline void Status::CopyFrom(const Status& other) { - upb_status_copy(this, &other); -} + /* Returns true if there is no error. */ + bool ok() const { return upb_ok(this); } -} /* namespace upb */ + /* Optional error space and code, useful if the caller wants to + * programmatically check the specific kind of error. */ + ErrorSpace* error_space() { return upb_status_errspace(this); } + int error_code() const { return upb_status_errcode(this); } + /* The returned string is invalidated by any other call into the status. */ + const char *error_message() const { return upb_status_errmsg(this); } + + /* The error message will be truncated if it is longer than + * UPB_STATUS_MAX_MESSAGE-4. */ + void SetErrorMessage(const char* msg) { upb_status_seterrmsg(this, msg); } + void SetFormattedErrorMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + upb_status_vseterrf(this, fmt, args); + va_end(args); + } + + /* Resets the status to a successful state with no message. */ + void Clear() { upb_status_clear(this); } + + void CopyFrom(const Status& other) { upb_status_copy(this, &other); } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Status) +#else +struct upb_status { #endif + bool ok_; + + /* Specific status code defined by some error space (optional). */ + int code_; + upb_errorspace *error_space_; + + /* TODO(haberman): add file/line of error? */ + + /* Error message; NULL-terminated. */ + char msg[UPB_STATUS_MAX_MESSAGE]; +}; + +#define UPB_STATUS_INIT {true, 0, NULL, {0}} + + +/** Built-in error spaces. ****************************************************/ + +/* Errors raised by upb that we want to be able to detect programmatically. */ +typedef enum { + UPB_NOMEM /* Can't reuse ENOMEM because it is POSIX, not ISO C. */ +} upb_errcode_t; + +extern upb_errorspace upb_upberr; + +void upb_upberr_setoom(upb_status *s); + +/* Since errno is defined by standard C, we define an error space for it in + * core upb. Other error spaces should be defined in other, platform-specific + * modules. */ + +extern upb_errorspace upb_errnoerr; + + +/** upb::Allocator ************************************************************/ + +/* A upb::Allocator is a possibly-stateful allocator object. + * + * It could either be an arena allocator (which doesn't require individual + * free() calls) or a regular malloc() (which does). The client must therefore + * free memory unless it knows that the allocator is an arena allocator. */ +UPB_DECLARE_TYPE(upb::Allocator, upb_alloc) + +/* A malloc()/free() function. + * If "size" is 0 then the function acts like free(), otherwise it acts like + * realloc(). Only "oldsize" bytes from a previous allocation are preserved. */ +typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size); + +#ifdef __cplusplus + +class upb::Allocator UPB_FINAL { + public: + Allocator() {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Allocator) + + public: +#else +struct upb_alloc { +#endif /* __cplusplus */ + upb_alloc_func *func; +}; + +UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) { + assert(size > 0); + return alloc->func(alloc, NULL, 0, size); +} + +UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + assert(size > 0); + return alloc->func(alloc, ptr, oldsize, size); +} + +UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) { + alloc->func(alloc, ptr, 0, 0); +} + +/* The global allocator used by upb. Uses the standard malloc()/free(). */ + +extern upb_alloc upb_alloc_global; + +/* Functions that hard-code the global malloc. + * + * We still get benefit because we can put custom logic into our global + * allocator, like injecting out-of-memory faults in debug/testing builds. */ + +UPB_INLINE void *upb_gmalloc(size_t size) { + return upb_malloc(&upb_alloc_global, size); +} + +UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) { + return upb_realloc(&upb_alloc_global, ptr, oldsize, size); +} + +UPB_INLINE void upb_gfree(void *ptr) { + upb_free(&upb_alloc_global, ptr); +} + +/* upb::Arena *****************************************************************/ + +/* upb::Arena is a specific allocator implementation that uses arena allocation. + * The user provides an allocator that will be used to allocate the underlying + * arena blocks. Arenas by nature do not require the individual allocations + * to be freed. However the Arena does allow users to register cleanup + * functions that will run when the arena is destroyed. + * + * A upb::Arena is *not* thread-safe. + * + * You could write a thread-safe arena allocator that satisfies the + * upb::Allocator interface, but it would not be as efficient for the + * single-threaded case. */ +UPB_DECLARE_TYPE(upb::Arena, upb_arena) + +typedef void upb_cleanup_func(void *ud); + +#define UPB_ARENA_BLOCK_OVERHEAD (sizeof(size_t)*4) + +UPB_BEGIN_EXTERN_C + +void upb_arena_init(upb_arena *a); +void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc); +void upb_arena_uninit(upb_arena *a); +upb_alloc *upb_arena_alloc(upb_arena *a); +bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud); +size_t upb_arena_bytesallocated(const upb_arena *a); +void upb_arena_setnextblocksize(upb_arena *a, size_t size); +void upb_arena_setmaxblocksize(upb_arena *a, size_t size); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +class upb::Arena { + public: + /* A simple arena with no initial memory block and the default allocator. */ + Arena() { upb_arena_init(this); } + + /* Constructs an arena with the given initial block which allocates blocks + * with the given allocator. The given allocator must outlive the Arena. + * + * If you pass NULL for the allocator it will default to the global allocator + * upb_alloc_global, and NULL/0 for the initial block will cause there to be + * no initial block. */ + Arena(void *mem, size_t len, Allocator* a) { + upb_arena_init2(this, mem, len, a); + } + + ~Arena() { upb_arena_uninit(this); } + + /* Sets the size of the next block the Arena will request (unless the + * requested allocation is larger). Each block will double in size until the + * max limit is reached. */ + void SetNextBlockSize(size_t size) { upb_arena_setnextblocksize(this, size); } + + /* Sets the maximum block size. No blocks larger than this will be requested + * from the underlying allocator unless individual arena allocations are + * larger. */ + void SetMaxBlockSize(size_t size) { upb_arena_setmaxblocksize(this, size); } + + /* Allows this arena to be used as a generic allocator. + * + * The arena does not need free() calls so when using Arena as an allocator + * it is safe to skip them. However they are no-ops so there is no harm in + * calling free() either. */ + Allocator* allocator() { return upb_arena_alloc(this); } + + /* Add a cleanup function to run when the arena is destroyed. + * Returns false on out-of-memory. */ + bool AddCleanup(upb_cleanup_func* func, void* ud) { + return upb_arena_addcleanup(this, func, ud); + } + + /* Total number of bytes that have been allocated. It is undefined what + * Realloc() does to this counter. */ + size_t BytesAllocated() const { + return upb_arena_bytesallocated(this); + } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Arena) + +#else +struct upb_arena { +#endif /* __cplusplus */ + /* We implement the allocator interface. + * This must be the first member of upb_arena! */ + upb_alloc alloc; + + /* Allocator to allocate arena blocks. We are responsible for freeing these + * when we are destroyed. */ + upb_alloc *block_alloc; + + size_t bytes_allocated; + size_t next_block_size; + size_t max_block_size; + + /* Linked list of blocks. Points to an arena_block, defined in env.c */ + void *block_head; + + /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ + void *cleanup_head; + + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; +}; + + +/* upb::Environment ***********************************************************/ + +/* A upb::Environment provides a means for injecting malloc and an + * error-reporting callback into encoders/decoders. This allows them to be + * independent of nearly all assumptions about their actual environment. + * + * It is also a container for allocating the encoders/decoders themselves that + * insulates clients from knowing their actual size. This provides ABI + * compatibility even if the size of the objects change. And this allows the + * structure definitions to be in the .c files instead of the .h files, making + * the .h files smaller and more readable. + * + * We might want to consider renaming this to "Pipeline" if/when the concept of + * a pipeline element becomes more formalized. */ +UPB_DECLARE_TYPE(upb::Environment, upb_env) + +/* A function that receives an error report from an encoder or decoder. The + * callback can return true to request that the error should be recovered, but + * if the error is not recoverable this has no effect. */ +typedef bool upb_error_func(void *ud, const upb_status *status); + +UPB_BEGIN_EXTERN_C + +void upb_env_init(upb_env *e); +void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc); +void upb_env_uninit(upb_env *e); + +void upb_env_initonly(upb_env *e); + +upb_arena *upb_env_arena(upb_env *e); +bool upb_env_ok(const upb_env *e); +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); + +/* Convenience wrappers around the methods of the contained arena. */ +void upb_env_reporterrorsto(upb_env *e, upb_status *s); +bool upb_env_reporterror(upb_env *e, const upb_status *s); +void *upb_env_malloc(upb_env *e, size_t size); +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); +void upb_env_free(upb_env *e, void *ptr); +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); +size_t upb_env_bytesallocated(const upb_env *e); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +class upb::Environment { + public: + /* The given Arena must outlive this environment. */ + Environment() { upb_env_initonly(this); } + + Environment(void *mem, size_t len, Allocator *a) : arena_(mem, len, a) { + upb_env_initonly(this); + } + + Arena* arena() { return upb_env_arena(this); } + + /* Set a custom error reporting function. */ + void SetErrorFunction(upb_error_func* func, void* ud) { + upb_env_seterrorfunc(this, func, ud); + } + + /* Set the error reporting function to simply copy the status to the given + * status and abort. */ + void ReportErrorsTo(Status* status) { upb_env_reporterrorsto(this, status); } + + /* Returns true if all allocations and AddCleanup() calls have succeeded, + * and no errors were reported with ReportError() (except ones that recovered + * successfully). */ + bool ok() const { return upb_env_ok(this); } + + /* Reports an error to this environment's callback, returning true if + * the caller should try to recover. */ + bool ReportError(const Status* status) { + return upb_env_reporterror(this, status); + } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Environment) + +#else +struct upb_env { +#endif /* __cplusplus */ + upb_arena arena_; + upb_error_func *error_func_; + void *error_ud_; + bool ok_; +}; + + +/* upb::InlinedArena **********************************************************/ +/* upb::InlinedEnvironment ****************************************************/ + +/* upb::InlinedArena and upb::InlinedEnvironment seed their arenas with a + * predefined amount of memory. No heap memory will be allocated until the + * initial block is exceeded. + * + * These types only exist in C++ */ + +#ifdef __cplusplus + +template class upb::InlinedArena : public upb::Arena { + public: + InlinedArena() : Arena(initial_block_, N, NULL) {} + explicit InlinedArena(Allocator* a) : Arena(initial_block_, N, a) {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(InlinedArena) + + char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD]; +}; + +template class upb::InlinedEnvironment : public upb::Environment { + public: + InlinedEnvironment() : Environment(initial_block_, N, NULL) {} + explicit InlinedEnvironment(Allocator *a) + : Environment(initial_block_, N, a) {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(InlinedEnvironment) + + char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD]; +}; + +#endif /* __cplusplus */ + + #endif /* UPB_H_ */ @@ -617,10 +814,14 @@ typedef struct { #endif /* Like strdup(), which isn't always available since it's not ANSI C. */ -char *upb_strdup(const char *s); +char *upb_strdup(const char *s, upb_alloc *a); /* Variant that works with a length-delimited rather than NULL-delimited string, * as supported by strtable. */ -char *upb_strdup2(const char *s, size_t len); +char *upb_strdup2(const char *s, size_t len, upb_alloc *a); + +UPB_INLINE char *upb_gstrdup(const char *s) { + return upb_strdup(s, &upb_alloc_global); +} UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val, upb_ctype_t ctype) { @@ -797,14 +998,40 @@ typedef struct { * initialize const hash tables. Then we cast away const when we have to. */ const upb_tabent *entries; + +#ifndef NDEBUG + /* This table's allocator. We make the user pass it in to every relevant + * function and only use this to check it in debug mode. We do this solely + * to keep upb_table as small as possible. This might seem slightly paranoid + * but the plan is to use upb_table for all map fields and extension sets in + * a forthcoming message representation, so there could be a lot of these. + * If this turns out to be too annoying later, we can change it (since this + * is an internal-only header file). */ + upb_alloc *alloc; +#endif } upb_table; +#ifdef NDEBUG +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries} +#else +# ifdef UPB_DEBUG_REFS +/* At the moment the only mutable tables we statically initialize are debug + * ref tables. */ +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries, &upb_alloc_debugrefs} +# else +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries, NULL} +# endif +#endif + typedef struct { upb_table t; } upb_strtable; #define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \ - {{count, mask, ctype, size_lg2, entries}} + {UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries)} #define UPB_EMPTY_STRTABLE_INIT(ctype) \ UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL) @@ -817,7 +1044,7 @@ typedef struct { } upb_inttable; #define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \ - {{count, mask, ctype, size_lg2, ent}, a, asize, acount} + {UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount} #define UPB_EMPTY_INTTABLE_INIT(ctype) \ UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0) @@ -857,10 +1084,26 @@ UPB_INLINE bool upb_arrhas(upb_tabval key) { /* Initialize and uninitialize a table, respectively. If memory allocation * failed, false is returned that the table is uninitialized. */ -bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype); -bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype); -void upb_inttable_uninit(upb_inttable *table); -void upb_strtable_uninit(upb_strtable *table); +bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a); +bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a); +void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a); +void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a); + +UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) { + return upb_inttable_init2(table, ctype, &upb_alloc_global); +} + +UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) { + return upb_strtable_init2(table, ctype, &upb_alloc_global); +} + +UPB_INLINE void upb_inttable_uninit(upb_inttable *table) { + upb_inttable_uninit2(table, &upb_alloc_global); +} + +UPB_INLINE void upb_strtable_uninit(upb_strtable *table) { + upb_strtable_uninit2(table, &upb_alloc_global); +} /* Returns the number of values in the table. */ size_t upb_inttable_count(const upb_inttable *t); @@ -875,9 +1118,20 @@ UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) { * * If a table resize was required but memory allocation failed, false is * returned and the table is unchanged. */ -bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val); -bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, - upb_value val); +bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val, + upb_alloc *a); +bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len, + upb_value val, upb_alloc *a); + +UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key, + upb_value val) { + return upb_inttable_insert2(t, key, val, &upb_alloc_global); +} + +UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key, + size_t len, upb_value val) { + return upb_strtable_insert3(t, key, len, val, &upb_alloc_global); +} /* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key, @@ -900,8 +1154,13 @@ UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key, /* Removes an item from the table. Returns true if the remove was successful, * and stores the removed item in *val if non-NULL. */ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val); -bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, - upb_value *val); +bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len, + upb_value *val, upb_alloc *alloc); + +UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key, + size_t len, upb_value *val) { + return upb_strtable_remove3(t, key, len, val, &upb_alloc_global); +} /* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key, @@ -916,19 +1175,33 @@ bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val); /* Handy routines for treating an inttable like a stack. May not be mixed with * other insert/remove calls. */ -bool upb_inttable_push(upb_inttable *t, upb_value val); +bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a); upb_value upb_inttable_pop(upb_inttable *t); +UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) { + return upb_inttable_push2(t, val, &upb_alloc_global); +} + /* Convenience routines for inttables with pointer keys. */ -bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val); +bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val, + upb_alloc *a); bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val); bool upb_inttable_lookupptr( const upb_inttable *t, const void *key, upb_value *val); +UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key, + upb_value val) { + return upb_inttable_insertptr2(t, key, val, &upb_alloc_global); +} + /* Optimizes the table for the current set of entries, for both memory use and * lookup time. Client should call this after all entries have been inserted; * inserting more entries is legal, but will likely require a table resize. */ -void upb_inttable_compact(upb_inttable *t); +void upb_inttable_compact2(upb_inttable *t, upb_alloc *a); + +UPB_INLINE void upb_inttable_compact(upb_inttable *t) { + upb_inttable_compact2(t, &upb_alloc_global); +} /* A special-case inlinable version of the lookup routine for 32-bit * integers. */ @@ -957,7 +1230,7 @@ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, } /* Exposed for testing only. */ -bool upb_strtable_resize(upb_strtable *t, size_t size_lg2); +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a); /* Iterators ******************************************************************/ @@ -1002,8 +1275,8 @@ typedef struct { void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t); void upb_strtable_next(upb_strtable_iter *i); bool upb_strtable_done(const upb_strtable_iter *i); -const char *upb_strtable_iter_key(upb_strtable_iter *i); -size_t upb_strtable_iter_keylength(upb_strtable_iter *i); +const char *upb_strtable_iter_key(const upb_strtable_iter *i); +size_t upb_strtable_iter_keylength(const upb_strtable_iter *i); upb_value upb_strtable_iter_value(const upb_strtable_iter *i); void upb_strtable_iter_setdone(upb_strtable_iter *i); bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, @@ -1056,7 +1329,10 @@ bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, /* #define UPB_DEBUG_REFS */ #ifdef __cplusplus -namespace upb { class RefCounted; } +namespace upb { +class RefCounted; +template class reffed_ptr; +} #endif UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted) @@ -1124,10 +1400,12 @@ struct upb_refcounted { }; #ifdef UPB_DEBUG_REFS -#define UPB_REFCOUNT_INIT(refs, ref2s) \ - {&static_refcount, NULL, NULL, 0, true, refs, ref2s} +extern upb_alloc upb_alloc_debugrefs; +#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ + {&static_refcount, NULL, vtbl, 0, true, refs, ref2s} #else -#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true} +#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ + {&static_refcount, NULL, vtbl, 0, true} #endif UPB_BEGIN_EXTERN_C @@ -1260,6 +1538,111 @@ inline void RefCounted::CheckRef(const void *owner) const { } /* namespace upb */ #endif + +/* upb::reffed_ptr ************************************************************/ + +#ifdef __cplusplus + +#include /* For std::swap(). */ + +/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a + * ref on whatever object it points to (if any). */ +template class upb::reffed_ptr { + public: + reffed_ptr() : ptr_(NULL) {} + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + reffed_ptr(U* val, const void* ref_donor = NULL) + : ptr_(upb::upcast(val)) { + if (ref_donor) { + assert(ptr_); + ptr_->DonateRef(ref_donor, this); + } else if (ptr_) { + ptr_->Ref(this); + } + } + + template + reffed_ptr(const reffed_ptr& other) + : ptr_(upb::upcast(other.get())) { + if (ptr_) ptr_->Ref(this); + } + + reffed_ptr(const reffed_ptr& other) + : ptr_(upb::upcast(other.get())) { + if (ptr_) ptr_->Ref(this); + } + + ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } + + template + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + /* TODO(haberman): add C++11 move construction/assignment for greater + * efficiency. */ + + void swap(reffed_ptr& other) { + if (ptr_ == other.ptr_) { + return; + } + + if (ptr_) ptr_->DonateRef(this, &other); + if (other.ptr_) other.ptr_->DonateRef(&other, this); + std::swap(ptr_, other.ptr_); + } + + T& operator*() const { + assert(ptr_); + return *ptr_; + } + + T* operator->() const { + assert(ptr_); + return ptr_; + } + + T* get() const { return ptr_; } + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + void reset(U* ptr = NULL, const void* ref_donor = NULL) { + reffed_ptr(ptr, ref_donor).swap(*this); + } + + template + reffed_ptr down_cast() { + return reffed_ptr(upb::down_cast(get())); + } + + template + reffed_ptr dyn_cast() { + return reffed_ptr(upb::dyn_cast(get())); + } + + /* Plain release() is unsafe; if we were the only owner, it would leak the + * object. Instead we provide this: */ + T* ReleaseTo(const void* new_owner) { + T* ret = NULL; + ptr_->DonateRef(this, new_owner); + std::swap(ret, ptr_); + return ret; + } + + private: + T* ptr_; +}; + +#endif /* __cplusplus */ + #endif /* UPB_REFCOUNT_H_ */ #ifdef __cplusplus @@ -1511,6 +1894,11 @@ typedef enum { UPB_DESCRIPTOR_TYPE_SINT64 = 18 } upb_descriptortype_t; +typedef enum { + UPB_SYNTAX_PROTO2 = 2, + UPB_SYNTAX_PROTO3 = 3 +} upb_syntax_t; + /* Maximum field number allowed for FieldDefs. This is an inherent limit of the * protobuf wire format. */ #define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) @@ -1902,6 +2290,10 @@ UPB_END_EXTERN_C typedef upb_inttable_iter upb_msg_field_iter; typedef upb_strtable_iter upb_msg_oneof_iter; +/* Well-known field tag numbers for map-entry messages. */ +#define UPB_MAPENTRY_KEY 1 +#define UPB_MAPENTRY_VALUE 2 + #ifdef __cplusplus /* Structure that describes a single .proto message type. @@ -1960,6 +2352,11 @@ class upb::MessageDef { bool AddOneof(OneofDef* o, Status* s); bool AddOneof(const reffed_ptr& o, Status* s); + upb_syntax_t syntax() const; + + /* Returns false if we don't support this syntax value. */ + bool set_syntax(upb_syntax_t syntax); + /* Set this to false to indicate that primitive fields should not have * explicit presence information associated with them. This will affect all * fields added to this message. Defaults to true. */ @@ -2150,15 +2547,20 @@ UPB_REFCOUNTED_CMETHODS(upb_msgdef, upb_msgdef_upcast2) bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status); +upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner); const char *upb_msgdef_fullname(const upb_msgdef *m); const char *upb_msgdef_name(const upb_msgdef *m); -bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); +int upb_msgdef_numoneofs(const upb_msgdef *m); +upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m); -upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner); bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, upb_status *s); bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, upb_status *s); +bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); +void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); +bool upb_msgdef_mapentry(const upb_msgdef *m); +bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax); /* Field lookup in a couple of different variations: * - itof = int to field @@ -2200,18 +2602,21 @@ UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len); } -void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); -bool upb_msgdef_mapentry(const upb_msgdef *m); +/* Lookup of either field or oneof by name. Returns whether either was found. + * If the return is true, then the found def will be set, and the non-found + * one set to NULL. */ +bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len, + const upb_fielddef **f, const upb_oneofdef **o); -/* Well-known field tag numbers for map-entry messages. */ -#define UPB_MAPENTRY_KEY 1 -#define UPB_MAPENTRY_VALUE 2 +UPB_INLINE bool upb_msgdef_lookupnamez(const upb_msgdef *m, const char *name, + const upb_fielddef **f, + const upb_oneofdef **o) { + return upb_msgdef_lookupname(m, name, strlen(name), f, o); +} -const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m, - const char *name); -int upb_msgdef_numoneofs(const upb_msgdef *m); - -/* upb_msg_field_iter i; +/* Iteration over fields and oneofs. For example: + * + * upb_msg_field_iter i; * for(upb_msg_field_begin(&i, m); * !upb_msg_field_done(&i); * upb_msg_field_next(&i)) { @@ -2523,11 +2928,6 @@ UPB_END_EXTERN_C /* upb::FileDef ***************************************************************/ -typedef enum { - UPB_SYNTAX_PROTO2 = 2, - UPB_SYNTAX_PROTO3 = 3 -} upb_syntax_t; - #ifdef __cplusplus /* Class that represents a .proto file with some things defined in it. @@ -2890,12 +3290,18 @@ inline const char *MessageDef::full_name() const { inline const char *MessageDef::name() const { return upb_msgdef_name(this); } +inline upb_syntax_t MessageDef::syntax() const { + return upb_msgdef_syntax(this); +} inline bool MessageDef::set_full_name(const char* fullname, Status* s) { return upb_msgdef_setfullname(this, fullname, s); } inline bool MessageDef::set_full_name(const std::string& fullname, Status* s) { return upb_msgdef_setfullname(this, upb_safecstr(fullname), s); } +inline bool MessageDef::set_syntax(upb_syntax_t syntax) { + return upb_msgdef_setsyntax(this, syntax); +} inline bool MessageDef::Freeze(Status* status) { return upb_msgdef_freeze(this, status); } @@ -3309,8 +3715,8 @@ struct upb_def { bool came_from_user; }; -#define UPB_DEF_INIT(name, type, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), name, NULL, type, false } +#define UPB_DEF_INIT(name, type, vtbl, refs, ref2s) \ + { UPB_REFCOUNT_INIT(vtbl, refs, ref2s), name, NULL, type, false } /* upb_fielddef ***************************************************************/ @@ -3350,12 +3756,14 @@ struct upb_fielddef { uint32_t index_; }; +extern const struct upb_refcounted_vtbl upb_fielddef_vtbl; + #define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ packed, name, num, msgdef, subdef, selector_base, \ index, defaultval, refs, ref2s) \ { \ - UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ - {subdef}, NULL, false, false, \ + UPB_DEF_INIT(name, UPB_DEF_FIELD, &upb_fielddef_vtbl, refs, ref2s), \ + defaultval, {msgdef}, {subdef}, NULL, false, false, \ type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \ lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \ } @@ -3371,32 +3779,26 @@ struct upb_msgdef { /* Tables for looking up fields by number and name. */ upb_inttable itof; /* int to field */ - upb_strtable ntof; /* name to field */ + upb_strtable ntof; /* name to field/oneof */ - /* Tables for looking up oneofs by name. */ - upb_strtable ntoo; /* name to oneof */ - - /* Is this a map-entry message? - * TODO: set this flag properly for static descriptors; regenerate - * descriptor.upb.c. */ + /* Is this a map-entry message? */ bool map_entry; - /* Whether this message has proto2 or proto3 semantics. - * TODO: set this flag properly for static descriptors; regenerate - * descriptor.upb.c. */ + /* Whether this message has proto2 or proto3 semantics. */ upb_syntax_t syntax; /* TODO(haberman): proper extension ranges (there can be multiple). */ }; +extern const struct upb_refcounted_vtbl upb_msgdef_vtbl; + /* TODO: also support static initialization of the oneofs table. This will be * needed if we compile in descriptors that contain oneofs. */ #define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \ - refs, ref2s) \ + map_entry, syntax, refs, ref2s) \ { \ - UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count, \ - submsg_field_count, itof, ntof, \ - UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false, true \ + UPB_DEF_INIT(name, UPB_DEF_MSG, &upb_fielddef_vtbl, refs, ref2s), \ + selector_count, submsg_field_count, itof, ntof, map_entry, syntax \ } @@ -3410,8 +3812,11 @@ struct upb_enumdef { int32_t defaultval; }; +extern const struct upb_refcounted_vtbl upb_enumdef_vtbl; + #define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \ - { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval } + { UPB_DEF_INIT(name, UPB_DEF_ENUM, &upb_enumdef_vtbl, refs, ref2s), ntoi, \ + iton, defaultval } /* upb_oneofdef ***************************************************************/ @@ -3425,8 +3830,10 @@ struct upb_oneofdef { const upb_msgdef *parent; }; +extern const struct upb_refcounted_vtbl upb_oneofdef_vtbl; + #define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), name, ntof, itof } + { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), name, ntof, itof } /* upb_symtab *****************************************************************/ @@ -3437,9 +3844,6 @@ struct upb_symtab { upb_strtable symtab; }; -#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), symtab } - struct upb_filedef { upb_refcounted base; @@ -3451,6 +3855,8 @@ struct upb_filedef { upb_inttable deps; }; +extern const struct upb_refcounted_vtbl upb_filedef_vtbl; + #endif /* UPB_STATICINIT_H_ */ /* ** upb::Handlers (upb_handlers) @@ -5392,267 +5798,6 @@ inline BytesHandler::~BytesHandler() {} #endif /* UPB_HANDLERS_H */ /* -** upb::Environment (upb_env) -** -** A upb::Environment provides a means for injecting malloc and an -** error-reporting callback into encoders/decoders. This allows them to be -** independent of nearly all assumptions about their actual environment. -** -** It is also a container for allocating the encoders/decoders themselves that -** insulates clients from knowing their actual size. This provides ABI -** compatibility even if the size of the objects change. And this allows the -** structure definitions to be in the .c files instead of the .h files, making -** the .h files smaller and more readable. -*/ - - -#ifndef UPB_ENV_H_ -#define UPB_ENV_H_ - -#ifdef __cplusplus -namespace upb { -class Environment; -class SeededAllocator; -} -#endif - -UPB_DECLARE_TYPE(upb::Environment, upb_env) -UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc) - -typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); -typedef void upb_cleanup_func(void *ud); -typedef bool upb_error_func(void *ud, const upb_status *status); - -#ifdef __cplusplus - -/* An environment is *not* thread-safe. */ -class upb::Environment { - public: - Environment(); - ~Environment(); - - /* Set a custom memory allocation function for the environment. May ONLY - * be called before any calls to Malloc()/Realloc()/AddCleanup() below. - * If this is not called, the system realloc() function will be used. - * The given user pointer "ud" will be passed to the allocation function. - * - * The allocation function will not receive corresponding "free" calls. it - * must ensure that the memory is valid for the lifetime of the Environment, - * but it may be reclaimed any time thereafter. The likely usage is that - * "ud" points to a stateful allocator, and that the allocator frees all - * memory, arena-style, when it is destroyed. In this case the allocator must - * outlive the Environment. Another possibility is that the allocation - * function returns GC-able memory that is guaranteed to be GC-rooted for the - * life of the Environment. */ - void SetAllocationFunction(upb_alloc_func* alloc, void* ud); - - template - void SetAllocator(T* allocator) { - SetAllocationFunction(allocator->GetAllocationFunction(), allocator); - } - - /* Set a custom error reporting function. */ - void SetErrorFunction(upb_error_func* func, void* ud); - - /* Set the error reporting function to simply copy the status to the given - * status and abort. */ - void ReportErrorsTo(Status* status); - - /* Returns true if all allocations and AddCleanup() calls have succeeded, - * and no errors were reported with ReportError() (except ones that recovered - * successfully). */ - bool ok() const; - - /* Functions for use by encoders/decoders. **********************************/ - - /* Reports an error to this environment's callback, returning true if - * the caller should try to recover. */ - bool ReportError(const Status* status); - - /* Allocate memory. Uses the environment's allocation function. - * - * There is no need to free(). All memory will be freed automatically, but is - * guaranteed to outlive the Environment. */ - void* Malloc(size_t size); - - /* Reallocate memory. Preserves "oldsize" bytes from the existing buffer - * Requires: oldsize <= existing_size. - * - * TODO(haberman): should we also enforce that oldsize <= size? */ - void* Realloc(void* ptr, size_t oldsize, size_t size); - - /* Add a cleanup function to run when the environment is destroyed. - * Returns false on out-of-memory. - * - * The first call to AddCleanup() after SetAllocationFunction() is guaranteed - * to return true -- this makes it possible to robustly set a cleanup handler - * for a custom allocation function. */ - bool AddCleanup(upb_cleanup_func* func, void* ud); - - /* Total number of bytes that have been allocated. It is undefined what - * Realloc() does to this counter. */ - size_t BytesAllocated() const; - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Environment) - -#else -struct upb_env { -#endif /* __cplusplus */ - - bool ok_; - size_t bytes_allocated; - - /* Alloc function. */ - upb_alloc_func *alloc; - void *alloc_ud; - - /* Error-reporting function. */ - upb_error_func *err; - void *err_ud; - - /* Userdata for default alloc func. */ - void *default_alloc_ud; - - /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ - void *cleanup_head; - - /* For future expansion, since the size of this struct is exposed to users. */ - void *future1; - void *future2; -}; - -UPB_BEGIN_EXTERN_C - -void upb_env_init(upb_env *e); -void upb_env_uninit(upb_env *e); -void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); -void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); -void upb_env_reporterrorsto(upb_env *e, upb_status *status); -bool upb_env_ok(const upb_env *e); -bool upb_env_reporterror(upb_env *e, const upb_status *status); -void *upb_env_malloc(upb_env *e, size_t size); -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); -size_t upb_env_bytesallocated(const upb_env *e); - -UPB_END_EXTERN_C - -#ifdef __cplusplus - -/* An allocator that allocates from an initial memory region (likely the stack) - * before falling back to another allocator. */ -class upb::SeededAllocator { - public: - SeededAllocator(void *mem, size_t len); - ~SeededAllocator(); - - /* Set a custom fallback memory allocation function for the allocator, to use - * once the initial region runs out. - * - * May ONLY be called before GetAllocationFunction(). If this is not - * called, the system realloc() will be the fallback allocator. */ - void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); - - /* Gets the allocation function for this allocator. */ - upb_alloc_func* GetAllocationFunction(); - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator) - -#else -struct upb_seededalloc { -#endif /* __cplusplus */ - - /* Fallback alloc function. */ - upb_alloc_func *alloc; - upb_cleanup_func *alloc_cleanup; - void *alloc_ud; - bool need_cleanup; - bool returned_allocfunc; - - /* Userdata for default alloc func. */ - void *default_alloc_ud; - - /* Pointers for the initial memory region. */ - char *mem_base; - char *mem_ptr; - char *mem_limit; - - /* For future expansion, since the size of this struct is exposed to users. */ - void *future1; - void *future2; -}; - -UPB_BEGIN_EXTERN_C - -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); -void upb_seededalloc_uninit(upb_seededalloc *a); -void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, - void *ud); -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); - -UPB_END_EXTERN_C - -#ifdef __cplusplus - -namespace upb { - -inline Environment::Environment() { - upb_env_init(this); -} -inline Environment::~Environment() { - upb_env_uninit(this); -} -inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, - void *ud) { - upb_env_setallocfunc(this, alloc, ud); -} -inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { - upb_env_seterrorfunc(this, func, ud); -} -inline void Environment::ReportErrorsTo(Status* status) { - upb_env_reporterrorsto(this, status); -} -inline bool Environment::ok() const { - return upb_env_ok(this); -} -inline bool Environment::ReportError(const Status* status) { - return upb_env_reporterror(this, status); -} -inline void *Environment::Malloc(size_t size) { - return upb_env_malloc(this, size); -} -inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { - return upb_env_realloc(this, ptr, oldsize, size); -} -inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { - return upb_env_addcleanup(this, func, ud); -} -inline size_t Environment::BytesAllocated() const { - return upb_env_bytesallocated(this); -} - -inline SeededAllocator::SeededAllocator(void *mem, size_t len) { - upb_seededalloc_init(this, mem, len); -} -inline SeededAllocator::~SeededAllocator() { - upb_seededalloc_uninit(this); -} -inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, - void *ud) { - upb_seededalloc_setfallbackalloc(this, alloc, ud); -} -inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { - return upb_seededalloc_getallocfunc(this); -} - -} /* namespace upb */ - -#endif /* __cplusplus */ - -#endif /* UPB_ENV_H_ */ -/* ** upb::Sink (upb_sink) ** upb::BytesSink (upb_bytessink) ** @@ -6529,7 +6674,11 @@ inline FileDef* Reader::file(size_t i) const { * actually storing protobufs. It only contains *defs* which * let you reflect over a protobuf *schema*. */ -/* This file was generated by upbc (the upb compiler). +/* This file was generated by upbc (the upb compiler) from the input + * file: + * + * upb/descriptor/descriptor.proto + * * Do not edit -- your changes will be discarded when the file is * regenerated. */ @@ -6818,347 +6967,347 @@ namespace upbdefs { namespace google { namespace protobuf { -class DescriptorProto : public upb::reffed_ptr { +class DescriptorProto : public ::upb::reffed_ptr { public: - DescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + DescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); } static DescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m); return DescriptorProto(m, &m); } - class ExtensionRange : public upb::reffed_ptr { + class ExtensionRange : public ::upb::reffed_ptr { public: - ExtensionRange(const upb::MessageDef* m, const void *ref_donor = NULL) + ExtensionRange(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); } static ExtensionRange get() { - const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m); return ExtensionRange(m, &m); } }; - class ReservedRange : public upb::reffed_ptr { + class ReservedRange : public ::upb::reffed_ptr { public: - ReservedRange(const upb::MessageDef* m, const void *ref_donor = NULL) + ReservedRange(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); } static ReservedRange get() { - const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m); return ReservedRange(m, &m); } }; }; -class EnumDescriptorProto : public upb::reffed_ptr { +class EnumDescriptorProto : public ::upb::reffed_ptr { public: - EnumDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + EnumDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); } static EnumDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m); return EnumDescriptorProto(m, &m); } }; -class EnumOptions : public upb::reffed_ptr { +class EnumOptions : public ::upb::reffed_ptr { public: - EnumOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + EnumOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_EnumOptions_is(m)); } static EnumOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m); return EnumOptions(m, &m); } }; -class EnumValueDescriptorProto : public upb::reffed_ptr { +class EnumValueDescriptorProto : public ::upb::reffed_ptr { public: - EnumValueDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + EnumValueDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); } static EnumValueDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m); return EnumValueDescriptorProto(m, &m); } }; -class EnumValueOptions : public upb::reffed_ptr { +class EnumValueOptions : public ::upb::reffed_ptr { public: - EnumValueOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + EnumValueOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_EnumValueOptions_is(m)); } static EnumValueOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m); return EnumValueOptions(m, &m); } }; -class FieldDescriptorProto : public upb::reffed_ptr { +class FieldDescriptorProto : public ::upb::reffed_ptr { public: - FieldDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + FieldDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); } static FieldDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m); return FieldDescriptorProto(m, &m); } - class Label : public upb::reffed_ptr { + class Label : public ::upb::reffed_ptr { public: - Label(const upb::EnumDef* e, const void *ref_donor = NULL) + Label(const ::upb::EnumDef* e, const void *ref_donor = NULL) : reffed_ptr(e, ref_donor) { assert(upbdefs_google_protobuf_FieldDescriptorProto_Label_is(e)); } static Label get() { - const upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e); + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e); return Label(e, &e); } }; - class Type : public upb::reffed_ptr { + class Type : public ::upb::reffed_ptr { public: - Type(const upb::EnumDef* e, const void *ref_donor = NULL) + Type(const ::upb::EnumDef* e, const void *ref_donor = NULL) : reffed_ptr(e, ref_donor) { assert(upbdefs_google_protobuf_FieldDescriptorProto_Type_is(e)); } static Type get() { - const upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e); + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e); return Type(e, &e); } }; }; -class FieldOptions : public upb::reffed_ptr { +class FieldOptions : public ::upb::reffed_ptr { public: - FieldOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + FieldOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); } static FieldOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m); return FieldOptions(m, &m); } - class CType : public upb::reffed_ptr { + class CType : public ::upb::reffed_ptr { public: - CType(const upb::EnumDef* e, const void *ref_donor = NULL) + CType(const ::upb::EnumDef* e, const void *ref_donor = NULL) : reffed_ptr(e, ref_donor) { assert(upbdefs_google_protobuf_FieldOptions_CType_is(e)); } static CType get() { - const upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e); + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e); return CType(e, &e); } }; - class JSType : public upb::reffed_ptr { + class JSType : public ::upb::reffed_ptr { public: - JSType(const upb::EnumDef* e, const void *ref_donor = NULL) + JSType(const ::upb::EnumDef* e, const void *ref_donor = NULL) : reffed_ptr(e, ref_donor) { assert(upbdefs_google_protobuf_FieldOptions_JSType_is(e)); } static JSType get() { - const upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e); + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e); return JSType(e, &e); } }; }; -class FileDescriptorProto : public upb::reffed_ptr { +class FileDescriptorProto : public ::upb::reffed_ptr { public: - FileDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + FileDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); } static FileDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m); return FileDescriptorProto(m, &m); } }; -class FileDescriptorSet : public upb::reffed_ptr { +class FileDescriptorSet : public ::upb::reffed_ptr { public: - FileDescriptorSet(const upb::MessageDef* m, const void *ref_donor = NULL) + FileDescriptorSet(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_FileDescriptorSet_is(m)); } static FileDescriptorSet get() { - const upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m); return FileDescriptorSet(m, &m); } }; -class FileOptions : public upb::reffed_ptr { +class FileOptions : public ::upb::reffed_ptr { public: - FileOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + FileOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_FileOptions_is(m)); } static FileOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m); return FileOptions(m, &m); } - class OptimizeMode : public upb::reffed_ptr { + class OptimizeMode : public ::upb::reffed_ptr { public: - OptimizeMode(const upb::EnumDef* e, const void *ref_donor = NULL) + OptimizeMode(const ::upb::EnumDef* e, const void *ref_donor = NULL) : reffed_ptr(e, ref_donor) { assert(upbdefs_google_protobuf_FileOptions_OptimizeMode_is(e)); } static OptimizeMode get() { - const upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e); + const ::upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e); return OptimizeMode(e, &e); } }; }; -class MessageOptions : public upb::reffed_ptr { +class MessageOptions : public ::upb::reffed_ptr { public: - MessageOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + MessageOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); } static MessageOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m); return MessageOptions(m, &m); } }; -class MethodDescriptorProto : public upb::reffed_ptr { +class MethodDescriptorProto : public ::upb::reffed_ptr { public: - MethodDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + MethodDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); } static MethodDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m); return MethodDescriptorProto(m, &m); } }; -class MethodOptions : public upb::reffed_ptr { +class MethodOptions : public ::upb::reffed_ptr { public: - MethodOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + MethodOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_MethodOptions_is(m)); } static MethodOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m); return MethodOptions(m, &m); } }; -class OneofDescriptorProto : public upb::reffed_ptr { +class OneofDescriptorProto : public ::upb::reffed_ptr { public: - OneofDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + OneofDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_OneofDescriptorProto_is(m)); } static OneofDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m); return OneofDescriptorProto(m, &m); } }; -class ServiceDescriptorProto : public upb::reffed_ptr { +class ServiceDescriptorProto : public ::upb::reffed_ptr { public: - ServiceDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL) + ServiceDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); } static ServiceDescriptorProto get() { - const upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m); return ServiceDescriptorProto(m, &m); } }; -class ServiceOptions : public upb::reffed_ptr { +class ServiceOptions : public ::upb::reffed_ptr { public: - ServiceOptions(const upb::MessageDef* m, const void *ref_donor = NULL) + ServiceOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_ServiceOptions_is(m)); } static ServiceOptions get() { - const upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m); return ServiceOptions(m, &m); } }; -class SourceCodeInfo : public upb::reffed_ptr { +class SourceCodeInfo : public ::upb::reffed_ptr { public: - SourceCodeInfo(const upb::MessageDef* m, const void *ref_donor = NULL) + SourceCodeInfo(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_SourceCodeInfo_is(m)); } static SourceCodeInfo get() { - const upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m); return SourceCodeInfo(m, &m); } - class Location : public upb::reffed_ptr { + class Location : public ::upb::reffed_ptr { public: - Location(const upb::MessageDef* m, const void *ref_donor = NULL) + Location(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); } static Location get() { - const upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m); return Location(m, &m); } }; }; -class UninterpretedOption : public upb::reffed_ptr { +class UninterpretedOption : public ::upb::reffed_ptr { public: - UninterpretedOption(const upb::MessageDef* m, const void *ref_donor = NULL) + UninterpretedOption(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); } static UninterpretedOption get() { - const upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m); return UninterpretedOption(m, &m); } - class NamePart : public upb::reffed_ptr { + class NamePart : public ::upb::reffed_ptr { public: - NamePart(const upb::MessageDef* m, const void *ref_donor = NULL) + NamePart(const ::upb::MessageDef* m, const void *ref_donor = NULL) : reffed_ptr(m, ref_donor) { assert(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); } static NamePart get() { - const upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m); + const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m); return NamePart(m, &m); } }; @@ -7178,7 +7327,6 @@ class UninterpretedOption : public upb::reffed_ptr { #ifndef UPB_DECODER_INT_H_ #define UPB_DECODER_INT_H_ -#include /* ** upb::pb::Decoder ** @@ -7278,7 +7426,7 @@ class upb::pb::DecoderMethod { * constructed. This hint may be an overestimate for some build configurations. * But if the decoder library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_PB_DECODER_SIZE 4408 +#define UPB_PB_DECODER_SIZE 4416 #ifdef __cplusplus @@ -8097,7 +8245,8 @@ extern "C" { #endif /* Loads a binary descriptor and returns a NULL-terminated array of unfrozen - * filedefs. The caller owns the returned array. */ + * filedefs. The caller owns the returned array, which must be freed with + * upb_gfree(). */ upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner, upb_status *status); @@ -8244,7 +8393,7 @@ UPB_DECLARE_DERIVED_TYPE(upb::json::ParserMethod, upb::RefCounted, * constructed. This hint may be an overestimate for some build configurations. * But if the parser library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_JSON_PARSER_SIZE 4104 +#define UPB_JSON_PARSER_SIZE 4112 #ifdef __cplusplus @@ -8357,7 +8506,7 @@ UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer) /* upb::json::Printer *********************************************************/ -#define UPB_JSON_PRINTER_SIZE 168 +#define UPB_JSON_PRINTER_SIZE 176 #ifdef __cplusplus diff --git a/ruby/lib/google/protobuf/repeated_field.rb b/ruby/lib/google/protobuf/repeated_field.rb index 16c843c07..0ad2060d4 100644 --- a/ruby/lib/google/protobuf/repeated_field.rb +++ b/ruby/lib/google/protobuf/repeated_field.rb @@ -69,8 +69,8 @@ module Google # relationship explicit instead of implicit def_delegators :to_ary, :&, :*, :-, :'<=>', - :assoc, :bsearch, :combination, :compact, :count, :cycle, - :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, + :assoc, :bsearch, :bsearch_index, :combination, :compact, :count, + :cycle, :dig, :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, :include?, :index, :inspect, :join, :pack, :permutation, :product, :pretty_print, :pretty_print_cycle, :rassoc, :repeated_combination, :repeated_permutation, :reverse, diff --git a/src/Makefile.am b/src/Makefile.am index 2ba0ef9b8..800655062 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -507,6 +507,8 @@ protoc_inputs = \ google/protobuf/unittest_preserve_unknown_enum.proto \ google/protobuf/unittest.proto \ google/protobuf/unittest_proto3_arena.proto \ + google/protobuf/unittest_proto3_arena_lite.proto \ + google/protobuf/unittest_proto3_lite.proto \ google/protobuf/unittest_well_known_types.proto \ google/protobuf/util/internal/testdata/anys.proto \ google/protobuf/util/internal/testdata/books.proto \ @@ -609,6 +611,10 @@ protoc_outputs = \ google/protobuf/unittest_preserve_unknown_enum.pb.h \ google/protobuf/unittest_proto3_arena.pb.cc \ google/protobuf/unittest_proto3_arena.pb.h \ + google/protobuf/unittest_proto3_arena_lite.pb.cc \ + google/protobuf/unittest_proto3_arena_lite.pb.h \ + google/protobuf/unittest_proto3_lite.pb.cc \ + google/protobuf/unittest_proto3_lite.pb.h \ google/protobuf/unittest_well_known_types.pb.cc \ google/protobuf/unittest_well_known_types.pb.h \ google/protobuf/util/internal/testdata/anys.pb.cc \ @@ -710,6 +716,8 @@ protobuf_test_SOURCES = \ google/protobuf/no_field_presence_test.cc \ google/protobuf/preserve_unknown_enum_test.cc \ google/protobuf/proto3_arena_unittest.cc \ + google/protobuf/proto3_arena_lite_unittest.cc \ + google/protobuf/proto3_lite_unittest.cc \ google/protobuf/reflection_ops_unittest.cc \ google/protobuf/repeated_field_reflection_unittest.cc \ google/protobuf/repeated_field_unittest.cc \ diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc index def2ec19a..cbeba3023 100644 --- a/src/google/protobuf/api.pb.cc +++ b/src/google/protobuf/api.pb.cc @@ -996,8 +996,16 @@ Method* Method::New(::google::protobuf::Arena* arena) const { void Method::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.Method) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Method, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc index fda518071..196b39dd9 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc @@ -757,10 +757,11 @@ bool HasNonZeroDefaultValue(const FieldDescriptor* field) { return false; } - if (!field->has_default_value()) { - // No custom default set in the proto file. - return false; - } + // As much as checking field->has_default_value() seems useful, it isn't + // because of enums. proto2 syntax allows the first item in an enum (the + // default) to be non zero. So checking field->has_default_value() would + // result in missing this non zero default. See MessageWithOneBasedEnum in + // objectivec/Tests/unittest_objc.proto for a test Message to confirm this. // Some proto file set the default to the zero value, so make sure the value // isn't the zero case. diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 50d1cc6e5..4d5c4d99d 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -2491,8 +2491,16 @@ DescriptorProto_ExtensionRange* DescriptorProto_ExtensionRange::New(::google::pr void DescriptorProto_ExtensionRange::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ExtensionRange) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(DescriptorProto_ExtensionRange, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -2782,8 +2790,16 @@ DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::New(::google::prot void DescriptorProto_ReservedRange::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ReservedRange) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(DescriptorProto_ReservedRange, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -7161,8 +7177,16 @@ MethodDescriptorProto* MethodDescriptorProto::New(::google::protobuf::Arena* are void MethodDescriptorProto::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.MethodDescriptorProto) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(MethodDescriptorProto, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -7965,8 +7989,16 @@ FileOptions* FileOptions::New(::google::protobuf::Arena* arena) const { void FileOptions::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.FileOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(FileOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -9360,8 +9392,16 @@ MessageOptions* MessageOptions::New(::google::protobuf::Arena* arena) const { void MessageOptions::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.MessageOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(MessageOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -9961,8 +10001,16 @@ FieldOptions* FieldOptions::New(::google::protobuf::Arena* arena) const { void FieldOptions::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.FieldOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(FieldOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -10645,8 +10693,16 @@ EnumOptions* EnumOptions::New(::google::protobuf::Arena* arena) const { void EnumOptions::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.EnumOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(EnumOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -12473,8 +12529,16 @@ UninterpretedOption* UninterpretedOption::New(::google::protobuf::Arena* arena) void UninterpretedOption::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.UninterpretedOption) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(UninterpretedOption, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -14338,8 +14402,16 @@ GeneratedCodeInfo_Annotation* GeneratedCodeInfo_Annotation::New(::google::protob void GeneratedCodeInfo_Annotation::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.GeneratedCodeInfo.Annotation) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(GeneratedCodeInfo_Annotation, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc index 42c2f7dc7..e3639346d 100644 --- a/src/google/protobuf/duration.pb.cc +++ b/src/google/protobuf/duration.pb.cc @@ -180,8 +180,16 @@ Duration* Duration::New(::google::protobuf::Arena* arena) const { void Duration::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.Duration) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Duration, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ diff --git a/src/google/protobuf/proto3_arena_lite_unittest.cc b/src/google/protobuf/proto3_arena_lite_unittest.cc new file mode 100644 index 000000000..0f18c027e --- /dev/null +++ b/src/google/protobuf/proto3_arena_lite_unittest.cc @@ -0,0 +1,164 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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 +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +using proto3_arena_lite_unittest::TestAllTypes; + +namespace protobuf { +namespace { +// We selectively set/check a few representative fields rather than all fields +// as this test is only expected to cover the basics of arena support. +void SetAllFields(TestAllTypes* m) { + m->set_optional_int32(100); + m->set_optional_string("asdf"); + m->set_optional_bytes("jkl;"); + m->mutable_optional_nested_message()->set_bb(42); + m->mutable_optional_foreign_message()->set_c(43); + m->set_optional_nested_enum( + proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->set_optional_foreign_enum( + proto3_arena_lite_unittest::FOREIGN_BAZ); + m->mutable_optional_lazy_message()->set_bb(45); + m->add_repeated_int32(100); + m->add_repeated_string("asdf"); + m->add_repeated_bytes("jkl;"); + m->add_repeated_nested_message()->set_bb(46); + m->add_repeated_foreign_message()->set_c(47); + m->add_repeated_nested_enum( + proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->add_repeated_foreign_enum( + proto3_arena_lite_unittest::FOREIGN_BAZ); + m->add_repeated_lazy_message()->set_bb(49); + + m->set_oneof_uint32(1); + m->mutable_oneof_nested_message()->set_bb(50); + m->set_oneof_string("test"); // only this one remains set +} + +void ExpectAllFieldsSet(const TestAllTypes& m) { + EXPECT_EQ(100, m.optional_int32()); + EXPECT_EQ("asdf", m.optional_string()); + EXPECT_EQ("jkl;", m.optional_bytes()); + EXPECT_EQ(true, m.has_optional_nested_message()); + EXPECT_EQ(42, m.optional_nested_message().bb()); + EXPECT_EQ(true, m.has_optional_foreign_message()); + EXPECT_EQ(43, m.optional_foreign_message().c()); + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.optional_nested_enum()); + EXPECT_EQ(proto3_arena_lite_unittest::FOREIGN_BAZ, + m.optional_foreign_enum()); + EXPECT_EQ(true, m.has_optional_lazy_message()); + EXPECT_EQ(45, m.optional_lazy_message().bb()); + + EXPECT_EQ(1, m.repeated_int32_size()); + EXPECT_EQ(100, m.repeated_int32(0)); + EXPECT_EQ(1, m.repeated_string_size()); + EXPECT_EQ("asdf", m.repeated_string(0)); + EXPECT_EQ(1, m.repeated_bytes_size()); + EXPECT_EQ("jkl;", m.repeated_bytes(0)); + EXPECT_EQ(1, m.repeated_nested_message_size()); + EXPECT_EQ(46, m.repeated_nested_message(0).bb()); + EXPECT_EQ(1, m.repeated_foreign_message_size()); + EXPECT_EQ(47, m.repeated_foreign_message(0).c()); + EXPECT_EQ(1, m.repeated_nested_enum_size()); + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.repeated_nested_enum(0)); + EXPECT_EQ(1, m.repeated_foreign_enum_size()); + EXPECT_EQ(proto3_arena_lite_unittest::FOREIGN_BAZ, + m.repeated_foreign_enum(0)); + EXPECT_EQ(1, m.repeated_lazy_message_size()); + EXPECT_EQ(49, m.repeated_lazy_message(0).bb()); + + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes::kOneofString, + m.oneof_field_case()); + EXPECT_EQ("test", m.oneof_string()); +} + +// In this file we only test some basic functionalities of arena support in +// proto3 and expect the arena support to be fully tested in proto2 unittests +// because proto3 shares most code with proto2. + +TEST(Proto3ArenaLiteTest, Parsing) { + TestAllTypes original; + SetAllFields(&original); + + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->ParseFromString(original.SerializeAsString()); + ExpectAllFieldsSet(*arena_message); +} + +TEST(Proto3ArenaLiteTest, Swap) { + Arena arena1; + Arena arena2; + + // Test Swap(). + TestAllTypes* arena1_message = Arena::CreateMessage(&arena1); + TestAllTypes* arena2_message = Arena::CreateMessage(&arena2); + arena1_message->Swap(arena2_message); + EXPECT_EQ(&arena1, arena1_message->GetArena()); + EXPECT_EQ(&arena2, arena2_message->GetArena()); +} + +TEST(Proto3ArenaLiteTest, SetAllocatedMessage) { + Arena arena; + TestAllTypes *arena_message = Arena::CreateMessage(&arena); + TestAllTypes::NestedMessage* nested = new TestAllTypes::NestedMessage; + nested->set_bb(118); + arena_message->set_allocated_optional_nested_message(nested); + EXPECT_EQ(118, arena_message->optional_nested_message().bb()); +} + +TEST(Proto3ArenaLiteTest, ReleaseMessage) { + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->mutable_optional_nested_message()->set_bb(118); + google::protobuf::scoped_ptr nested( + arena_message->release_optional_nested_message()); + EXPECT_EQ(118, nested->bb()); +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/proto3_lite_unittest.cc b/src/google/protobuf/proto3_lite_unittest.cc new file mode 100644 index 000000000..2e2beea90 --- /dev/null +++ b/src/google/protobuf/proto3_lite_unittest.cc @@ -0,0 +1,145 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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 +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +using proto3_lite_unittest::TestAllTypes; + +namespace protobuf { +namespace { +// We selectively set/check a few representative fields rather than all fields +// as this test is only expected to cover the basics of lite support. +void SetAllFields(TestAllTypes* m) { + m->set_optional_int32(100); + m->set_optional_string("asdf"); + m->set_optional_bytes("jkl;"); + m->mutable_optional_nested_message()->set_bb(42); + m->mutable_optional_foreign_message()->set_c(43); + m->set_optional_nested_enum( + proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->set_optional_foreign_enum( + proto3_lite_unittest::FOREIGN_BAZ); + m->mutable_optional_lazy_message()->set_bb(45); + m->add_repeated_int32(100); + m->add_repeated_string("asdf"); + m->add_repeated_bytes("jkl;"); + m->add_repeated_nested_message()->set_bb(46); + m->add_repeated_foreign_message()->set_c(47); + m->add_repeated_nested_enum( + proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->add_repeated_foreign_enum( + proto3_lite_unittest::FOREIGN_BAZ); + m->add_repeated_lazy_message()->set_bb(49); + + m->set_oneof_uint32(1); + m->mutable_oneof_nested_message()->set_bb(50); + m->set_oneof_string("test"); // only this one remains set +} + +void ExpectAllFieldsSet(const TestAllTypes& m) { + EXPECT_EQ(100, m.optional_int32()); + EXPECT_EQ("asdf", m.optional_string()); + EXPECT_EQ("jkl;", m.optional_bytes()); + EXPECT_EQ(true, m.has_optional_nested_message()); + EXPECT_EQ(42, m.optional_nested_message().bb()); + EXPECT_EQ(true, m.has_optional_foreign_message()); + EXPECT_EQ(43, m.optional_foreign_message().c()); + EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.optional_nested_enum()); + EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ, + m.optional_foreign_enum()); + EXPECT_EQ(true, m.has_optional_lazy_message()); + EXPECT_EQ(45, m.optional_lazy_message().bb()); + + EXPECT_EQ(1, m.repeated_int32_size()); + EXPECT_EQ(100, m.repeated_int32(0)); + EXPECT_EQ(1, m.repeated_string_size()); + EXPECT_EQ("asdf", m.repeated_string(0)); + EXPECT_EQ(1, m.repeated_bytes_size()); + EXPECT_EQ("jkl;", m.repeated_bytes(0)); + EXPECT_EQ(1, m.repeated_nested_message_size()); + EXPECT_EQ(46, m.repeated_nested_message(0).bb()); + EXPECT_EQ(1, m.repeated_foreign_message_size()); + EXPECT_EQ(47, m.repeated_foreign_message(0).c()); + EXPECT_EQ(1, m.repeated_nested_enum_size()); + EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.repeated_nested_enum(0)); + EXPECT_EQ(1, m.repeated_foreign_enum_size()); + EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ, + m.repeated_foreign_enum(0)); + EXPECT_EQ(1, m.repeated_lazy_message_size()); + EXPECT_EQ(49, m.repeated_lazy_message(0).bb()); + + EXPECT_EQ(proto3_lite_unittest::TestAllTypes::kOneofString, + m.oneof_field_case()); + EXPECT_EQ("test", m.oneof_string()); +} + +// In this file we only test some basic functionalities of in proto3 and expect +// the rest is fully tested in proto2 unittests because proto3 shares most code +// with proto2. + +TEST(Proto3LiteTest, Parsing) { + TestAllTypes original; + SetAllFields(&original); + + TestAllTypes msg; + msg.ParseFromString(original.SerializeAsString()); + ExpectAllFieldsSet(msg); +} + +TEST(Proto3LiteTest, Swap) { + // Test Swap(). + TestAllTypes msg1; + TestAllTypes msg2; + msg1.set_optional_string("123"); + msg2.set_optional_string("3456"); + msg1.Swap(&msg2); + EXPECT_EQ("3456", msg1.optional_string()); + EXPECT_EQ("123", msg2.optional_string()); + EXPECT_EQ(msg1.ByteSize(), msg2.ByteSize() + 1); +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc index 9b8eeba2c..7cdf5093d 100644 --- a/src/google/protobuf/timestamp.pb.cc +++ b/src/google/protobuf/timestamp.pb.cc @@ -194,8 +194,16 @@ Timestamp* Timestamp::New(::google::protobuf::Arena* arena) const { void Timestamp::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.Timestamp) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Timestamp, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc index d47375287..759cab278 100644 --- a/src/google/protobuf/type.pb.cc +++ b/src/google/protobuf/type.pb.cc @@ -1116,8 +1116,16 @@ Field* Field::New(::google::protobuf::Arena* arena) const { void Field::Clear() { // @@protoc_insertion_point(message_clear_start:google.protobuf.Field) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Field, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ diff --git a/src/google/protobuf/unittest_proto3_arena_lite.proto b/src/google/protobuf/unittest_proto3_arena_lite.proto new file mode 100644 index 000000000..5a60b90f5 --- /dev/null +++ b/src/google/protobuf/unittest_proto3_arena_lite.proto @@ -0,0 +1,207 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +syntax = "proto3"; + +option cc_enable_arenas = true; +option optimize_for = LITE_RUNTIME; + +import "google/protobuf/unittest_import.proto"; + +package proto3_arena_lite_unittest; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + int32 bb = 1; + } + + enum NestedEnum { + ZERO = 0; + FOO = 1; + BAR = 2; + BAZ = 3; + NEG = -1; // Intentionally negative. + } + + // Singular + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + // Defined in unittest_import_public.proto + protobuf_unittest_import.PublicImportMessage + optional_public_import_message = 26; + + NestedMessage optional_lazy_message = 27 [lazy=true]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + repeated NestedMessage repeated_lazy_message = 57 [lazy=true]; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +// Test messages for packed fields + +message TestPackedTypes { + repeated int32 packed_int32 = 90 [packed = true]; + repeated int64 packed_int64 = 91 [packed = true]; + repeated uint32 packed_uint32 = 92 [packed = true]; + repeated uint64 packed_uint64 = 93 [packed = true]; + repeated sint32 packed_sint32 = 94 [packed = true]; + repeated sint64 packed_sint64 = 95 [packed = true]; + repeated fixed32 packed_fixed32 = 96 [packed = true]; + repeated fixed64 packed_fixed64 = 97 [packed = true]; + repeated sfixed32 packed_sfixed32 = 98 [packed = true]; + repeated sfixed64 packed_sfixed64 = 99 [packed = true]; + repeated float packed_float = 100 [packed = true]; + repeated double packed_double = 101 [packed = true]; + repeated bool packed_bool = 102 [packed = true]; + repeated ForeignEnum packed_enum = 103 [packed = true]; +} + +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + +// This proto includes a recusively nested message. +message NestedTestAllTypes { + NestedTestAllTypes child = 1; + TestAllTypes payload = 2; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_ZERO = 0; + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +// TestEmptyMessage is used to test behavior of unknown fields. +message TestEmptyMessage { +} + diff --git a/src/google/protobuf/unittest_proto3_lite.proto b/src/google/protobuf/unittest_proto3_lite.proto new file mode 100644 index 000000000..874ade6c0 --- /dev/null +++ b/src/google/protobuf/unittest_proto3_lite.proto @@ -0,0 +1,206 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; + +import "google/protobuf/unittest_import.proto"; + +package proto3_lite_unittest; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + int32 bb = 1; + } + + enum NestedEnum { + ZERO = 0; + FOO = 1; + BAR = 2; + BAZ = 3; + NEG = -1; // Intentionally negative. + } + + // Singular + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + // Defined in unittest_import_public.proto + protobuf_unittest_import.PublicImportMessage + optional_public_import_message = 26; + + NestedMessage optional_lazy_message = 27 [lazy=true]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + repeated NestedMessage repeated_lazy_message = 57 [lazy=true]; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +// Test messages for packed fields + +message TestPackedTypes { + repeated int32 packed_int32 = 90 [packed = true]; + repeated int64 packed_int64 = 91 [packed = true]; + repeated uint32 packed_uint32 = 92 [packed = true]; + repeated uint64 packed_uint64 = 93 [packed = true]; + repeated sint32 packed_sint32 = 94 [packed = true]; + repeated sint64 packed_sint64 = 95 [packed = true]; + repeated fixed32 packed_fixed32 = 96 [packed = true]; + repeated fixed64 packed_fixed64 = 97 [packed = true]; + repeated sfixed32 packed_sfixed32 = 98 [packed = true]; + repeated sfixed64 packed_sfixed64 = 99 [packed = true]; + repeated float packed_float = 100 [packed = true]; + repeated double packed_double = 101 [packed = true]; + repeated bool packed_bool = 102 [packed = true]; + repeated ForeignEnum packed_enum = 103 [packed = true]; +} + +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + +// This proto includes a recusively nested message. +message NestedTestAllTypes { + NestedTestAllTypes child = 1; + TestAllTypes payload = 2; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_ZERO = 0; + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +// TestEmptyMessage is used to test behavior of unknown fields. +message TestEmptyMessage { +} +