Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

62
v8/LICENSE.md Normal file
View File

@ -0,0 +1,62 @@
This license applies to all parts of V8 that are not externally
maintained libraries. The externally maintained libraries used by V8
are:
- PCRE test suite, located in
test/mjsunit/third_party/regexp-pcre/regexp-pcre.js. This is based on the
test suite from PCRE-7.3, which is copyrighted by the University
of Cambridge and Google, Inc. The copyright notice and license
are embedded in regexp-pcre.js.
- Layout tests, located in test/mjsunit/third_party/object-keys. These are
based on layout tests from webkit.org which are copyrighted by
Apple Computer, Inc. and released under a 3-clause BSD license.
- Strongtalk assembler, the basis of the files assembler-arm-inl.h,
assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h,
assembler-x64.cc, assembler-x64.h, assembler-mips-inl.h,
assembler-mips.cc, assembler-mips.h, assembler.cc and assembler.h.
This code is copyrighted by Sun Microsystems Inc. and released
under a 3-clause BSD license.
- Valgrind client API header, located at src/third_party/valgrind/valgrind.h
This is released under the BSD license.
- The Wasm C/C++ API headers, located at third_party/wasm-api/wasm.{h,hh}
This is released under the Apache license. The API's upstream prototype
implementation also formed the basis of V8's implementation in
src/wasm/c-api.cc.
These libraries have their own licenses; we recommend you read them,
as their terms may differ from the terms below.
Further license information can be found in LICENSE files located in
sub-directories.
Copyright 2014, the V8 project authors. All rights reserved.
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.

72
v8/include/APIDesign.md Normal file
View File

@ -0,0 +1,72 @@
# The V8 public C++ API
# Overview
The V8 public C++ API aims to support four use cases:
1. Enable applications that embed V8 (called the embedder) to configure and run
one or more instances of V8.
2. Expose ECMAScript-like capabilities to the embedder.
3. Enable the embedder to interact with ECMAScript by exposing API objects.
4. Provide access to the V8 debugger (inspector).
# Configuring and running an instance of V8
V8 requires access to certain OS-level primitives such as the ability to
schedule work on threads, or allocate memory.
The embedder can define how to access those primitives via the v8::Platform
interface. While V8 bundles a basic implementation, embedders are highly
encouraged to implement v8::Platform themselves.
Currently, the v8::ArrayBuffer::Allocator is passed to the v8::Isolate factory
method, however, conceptually it should also be part of the v8::Platform since
all instances of V8 should share one allocator.
Once the v8::Platform is configured, an v8::Isolate can be created. All
further interactions with V8 should explicitly reference the v8::Isolate they
refer to. All API methods should eventually take an v8::Isolate parameter.
When a given instance of V8 is no longer needed, it can be destroyed by
disposing the respective v8::Isolate. If the embedder wishes to free all memory
associated with the v8::Isolate, it has to first clear all global handles
associated with that v8::Isolate.
# ECMAScript-like capabilities
In general, the C++ API shouldn't enable capabilities that aren't available to
scripts running in V8. Experience has shown that it's not possible to maintain
such API methods in the long term. However, capabilities also available to
scripts, i.e., ones that are defined in the ECMAScript standard are there to
stay, and we can safely expose them to embedders.
The C++ API should also be pleasant to use, and not require learning new
paradigms. Similarly to how the API exposed to scripts aims to provide good
ergonomics, we should aim to provide a reasonable developer experience for this
API surface.
ECMAScript makes heavy use of exceptions, however, V8's C++ code doesn't use
C++ exceptions. Therefore, all API methods that can throw exceptions should
indicate so by returning a v8::Maybe<> or v8::MaybeLocal<> result,
and by taking a v8::Local<v8::Context> parameter that indicates in which
context a possible exception should be thrown.
# API objects
V8 allows embedders to define special objects that expose additional
capabilities and APIs to scripts. The most prominent example is exposing the
HTML DOM in Blink. Other examples are e.g. node.js. It is less clear what kind
of capabilities we want to expose via this API surface. As a rule of thumb, we
want to expose operations as defined in the WebIDL and HTML spec: we
assume that those requirements are somewhat stable, and that they are a
superset of the requirements of other embedders including node.js.
Ideally, the API surfaces defined in those specs hook into the ECMAScript spec
which in turn guarantees long-term stability of the API.
# The V8 inspector
All debugging capabilities of V8 should be exposed via the inspector protocol.
The exception to this are profiling features exposed via v8-profiler.h.
Changes to the inspector protocol need to ensure backwards compatibility and
commitment to maintain.

10
v8/include/DEPS Normal file
View File

@ -0,0 +1,10 @@
include_rules = [
# v8-inspector-protocol.h depends on generated files under include/inspector.
"+inspector",
"+cppgc/common.h",
# Used by v8-cppgc.h to bridge to cppgc.
"+cppgc/custom-space.h",
"+cppgc/heap-statistics.h",
"+cppgc/internal/write-barrier.h",
"+cppgc/visitor.h",
]

11
v8/include/DIR_METADATA Normal file
View File

@ -0,0 +1,11 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>API"
}

23
v8/include/OWNERS Normal file
View File

@ -0,0 +1,23 @@
adamk@chromium.org
cbruni@chromium.org
leszeks@chromium.org
mlippautz@chromium.org
verwaest@chromium.org
yangguo@chromium.org
per-file *DEPS=file:../COMMON_OWNERS
per-file v8-internal.h=file:../COMMON_OWNERS
per-file v8-debug.h=file:../src/debug/OWNERS
per-file js_protocol.pdl=file:../src/inspector/OWNERS
per-file v8-inspector*=file:../src/inspector/OWNERS
per-file v8-inspector*=file:../src/inspector/OWNERS
# Needed by the auto_tag builder
per-file v8-version.h=v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com
# For branch updates:
per-file v8-version.h=file:../INFRA_OWNERS
per-file v8-version.h=hablich@chromium.org
per-file v8-version.h=vahl@chromium.org

8
v8/include/cppgc/DEPS Normal file
View File

@ -0,0 +1,8 @@
include_rules = [
"-include",
"+v8config.h",
"+v8-platform.h",
"+cppgc",
"-src",
"+libplatform/libplatform.h",
]

2
v8/include/cppgc/OWNERS Normal file
View File

@ -0,0 +1,2 @@
bikineev@chromium.org
omerkatz@chromium.org

133
v8/include/cppgc/README.md Normal file
View File

@ -0,0 +1,133 @@
# Oilpan: C++ Garbage Collection
Oilpan is an open-source garbage collection library for C++ that can be used stand-alone or in collaboration with V8's JavaScript garbage collector.
Oilpan implements mark-and-sweep garbage collection (GC) with limited compaction (for a subset of objects).
**Key properties**
- Trace-based garbage collection;
- Incremental and concurrent marking;
- Incremental and concurrent sweeping;
- Precise on-heap memory layout;
- Conservative on-stack memory layout;
- Allows for collection with and without considering stack;
- Non-incremental and non-concurrent compaction for selected spaces;
See the [Hello World](https://chromium.googlesource.com/v8/v8/+/main/samples/cppgc/hello-world.cc) example on how to get started using Oilpan to manage C++ code.
Oilpan follows V8's project organization, see e.g. on how we accept [contributions](https://v8.dev/docs/contribute) and [provide a stable API](https://v8.dev/docs/api).
## Threading model
Oilpan features thread-local garbage collection and assumes heaps are not shared among threads.
In other words, objects are accessed and ultimately reclaimed by the garbage collector on the same thread that allocates them.
This allows Oilpan to run garbage collection in parallel with mutators running in other threads.
References to objects belonging to another thread's heap are modeled using cross-thread roots.
This is even true for on-heap to on-heap references.
## Heap partitioning
Oilpan's heaps are partitioned into spaces.
The space for an object is chosen depending on a number of criteria, e.g.:
- Objects over 64KiB are allocated in a large object space
- Objects can be assigned to a dedicated custom space.
Custom spaces can also be marked as compactable.
- Other objects are allocated in one of the normal page spaces bucketed depending on their size.
## Precise and conservative garbage collection
Oilpan supports two kinds of GCs:
1. **Conservative GC.**
A GC is called conservative when it is executed while the regular native stack is not empty.
In this case, the native stack might contain references to objects in Oilpan's heap, which should be kept alive.
The GC scans the native stack and treats the pointers discovered via the native stack as part of the root set.
This kind of GC is considered imprecise because values on stack other than references may accidentally appear as references to on-heap object, which means these objects will be kept alive despite being in practice unreachable from the application as an actual reference.
2. **Precise GC.**
A precise GC is triggered at the end of an event loop, which is controlled by an embedder via a platform.
At this point, it is guaranteed that there are no on-stack references pointing to Oilpan's heap.
This means there is no risk of confusing other value types with references.
Oilpan has precise knowledge of on-heap object layouts, and so it knows exactly where pointers lie in memory.
Oilpan can just start marking from the regular root set and collect all garbage precisely.
## Atomic, incremental and concurrent garbage collection
Oilpan has three modes of operation:
1. **Atomic GC.**
The entire GC cycle, including all its phases (e.g. see [Marking](#Marking-phase) and [Sweeping](#Sweeping-phase)), are executed back to back in a single pause.
This mode of operation is also known as Stop-The-World (STW) garbage collection.
It results in the most jank (due to a single long pause), but is overall the most efficient (e.g. no need for write barriers).
2. **Incremental GC.**
Garbage collection work is split up into multiple steps which are interleaved with the mutator, i.e. user code chunked into tasks.
Each step is a small chunk of work that is executed either as dedicated tasks between mutator tasks or, as needed, during mutator tasks.
Using incremental GC introduces the need for write barriers that record changes to the object graph so that a consistent state is observed and no objects are accidentally considered dead and reclaimed.
The incremental steps are followed by a smaller atomic pause to finalize garbage collection.
The smaller pause times, due to smaller chunks of work, helps with reducing jank.
3. **Concurrent GC.**
This is the most common type of GC.
It builds on top of incremental GC and offloads much of the garbage collection work away from the mutator thread and on to background threads.
Using concurrent GC allows the mutator thread to spend less time on GC and more on the actual mutator.
## Marking phase
The marking phase consists of the following steps:
1. Mark all objects in the root set.
2. Mark all objects transitively reachable from the root set by calling `Trace()` methods defined on each object.
3. Clear out all weak handles to unreachable objects and run weak callbacks.
The marking phase can be executed atomically in a stop-the-world manner, in which all 3 steps are executed one after the other.
Alternatively, it can also be executed incrementally/concurrently.
With incremental/concurrent marking, step 1 is executed in a short pause after which the mutator regains control.
Step 2 is repeatedly executed in an interleaved manner with the mutator.
When the GC is ready to finalize, i.e. step 2 is (almost) finished, another short pause is triggered in which step 2 is finished and step 3 is performed.
To prevent a user-after-free (UAF) issues it is required for Oilpan to know about all edges in the object graph.
This means that all pointers except on-stack pointers must be wrapped with Oilpan's handles (i.e., Persistent<>, Member<>, WeakMember<>).
Raw pointers to on-heap objects create an edge that Oilpan cannot observe and cause UAF issues
Thus, raw pointers shall not be used to reference on-heap objects (except for raw pointers on native stacks).
## Sweeping phase
The sweeping phase consists of the following steps:
1. Invoke pre-finalizers.
At this point, no destructors have been invoked and no memory has been reclaimed.
Pre-finalizers are allowed to access any other on-heap objects, even those that may get destructed.
2. Sweeping invokes destructors of the dead (unreachable) objects and reclaims memory to be reused by future allocations.
Assumptions should not be made about the order and the timing of their execution.
There is no guarantee on the order in which the destructors are invoked.
That's why destructors must not access any other on-heap objects (which might have already been destructed).
If some destructor unavoidably needs to access other on-heap objects, it will have to be converted to a pre-finalizer.
The pre-finalizer is allowed to access other on-heap objects.
The mutator is resumed before all destructors have ran.
For example, imagine a case where X is a client of Y, and Y holds a list of clients.
If the code relies on X's destructor removing X from the list, there is a risk that Y iterates the list and calls some method of X which may touch other on-heap objects.
This causes a use-after-free.
Care must be taken to make sure that X is explicitly removed from the list before the mutator resumes its execution in a way that doesn't rely on X's destructor (e.g. a pre-finalizer).
Similar to marking, sweeping can be executed in either an atomic stop-the-world manner or incrementally/concurrently.
With incremental/concurrent sweeping, step 2 is interleaved with mutator.
Incremental/concurrent sweeping can be atomically finalized in case it is needed to trigger another GC cycle.
Even with concurrent sweeping, destructors are guaranteed to run on the thread the object has been allocated on to preserve C++ semantics.
Notes:
* Weak processing runs only when the holder object of the WeakMember outlives the pointed object.
If the holder object and the pointed object die at the same time, weak processing doesn't run.
It is wrong to write code assuming that the weak processing always runs.
* Pre-finalizers are heavy because the thread needs to scan all pre-finalizers at each sweeping phase to determine which pre-finalizers should be invoked (the thread needs to invoke pre-finalizers of dead objects).
Adding pre-finalizers to frequently created objects should be avoided.

View File

@ -0,0 +1,310 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_ALLOCATION_H_
#define INCLUDE_CPPGC_ALLOCATION_H_
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <new>
#include <type_traits>
#include <utility>
#include "cppgc/custom-space.h"
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/gc-info.h"
#include "cppgc/type-traits.h"
#include "v8config.h" // NOLINT(build/include_directory)
#if defined(__has_attribute)
#if __has_attribute(assume_aligned)
#define CPPGC_DEFAULT_ALIGNED \
__attribute__((assume_aligned(api_constants::kDefaultAlignment)))
#define CPPGC_DOUBLE_WORD_ALIGNED \
__attribute__((assume_aligned(2 * api_constants::kDefaultAlignment)))
#endif // __has_attribute(assume_aligned)
#endif // defined(__has_attribute)
#if !defined(CPPGC_DEFAULT_ALIGNED)
#define CPPGC_DEFAULT_ALIGNED
#endif
#if !defined(CPPGC_DOUBLE_WORD_ALIGNED)
#define CPPGC_DOUBLE_WORD_ALIGNED
#endif
namespace cppgc {
/**
* AllocationHandle is used to allocate garbage-collected objects.
*/
class AllocationHandle;
namespace internal {
// Similar to C++17 std::align_val_t;
enum class AlignVal : size_t {};
class V8_EXPORT MakeGarbageCollectedTraitInternal {
protected:
static inline void MarkObjectAsFullyConstructed(const void* payload) {
// See api_constants for an explanation of the constants.
std::atomic<uint16_t>* atomic_mutable_bitfield =
reinterpret_cast<std::atomic<uint16_t>*>(
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(
reinterpret_cast<const uint8_t*>(payload) -
api_constants::kFullyConstructedBitFieldOffsetFromPayload)));
// It's safe to split use load+store here (instead of a read-modify-write
// operation), since it's guaranteed that this 16-bit bitfield is only
// modified by a single thread. This is cheaper in terms of code bloat (on
// ARM) and performance.
uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed);
value |= api_constants::kFullyConstructedBitMask;
atomic_mutable_bitfield->store(value, std::memory_order_release);
}
// Dispatch based on compile-time information.
//
// Default implementation is for a custom space with >`kDefaultAlignment` byte
// alignment.
template <typename GCInfoType, typename CustomSpace, size_t alignment>
struct AllocationDispatcher final {
static void* Invoke(AllocationHandle& handle, size_t size) {
static_assert(std::is_base_of<CustomSpaceBase, CustomSpace>::value,
"Custom space must inherit from CustomSpaceBase.");
static_assert(
!CustomSpace::kSupportsCompaction,
"Custom spaces that support compaction do not support allocating "
"objects with non-default (i.e. word-sized) alignment.");
return MakeGarbageCollectedTraitInternal::Allocate(
handle, size, static_cast<AlignVal>(alignment),
internal::GCInfoTrait<GCInfoType>::Index(), CustomSpace::kSpaceIndex);
}
};
// Fast path for regular allocations for the default space with
// `kDefaultAlignment` byte alignment.
template <typename GCInfoType>
struct AllocationDispatcher<GCInfoType, void,
api_constants::kDefaultAlignment>
final {
static void* Invoke(AllocationHandle& handle, size_t size) {
return MakeGarbageCollectedTraitInternal::Allocate(
handle, size, internal::GCInfoTrait<GCInfoType>::Index());
}
};
// Default space with >`kDefaultAlignment` byte alignment.
template <typename GCInfoType, size_t alignment>
struct AllocationDispatcher<GCInfoType, void, alignment> final {
static void* Invoke(AllocationHandle& handle, size_t size) {
return MakeGarbageCollectedTraitInternal::Allocate(
handle, size, static_cast<AlignVal>(alignment),
internal::GCInfoTrait<GCInfoType>::Index());
}
};
// Custom space with `kDefaultAlignment` byte alignment.
template <typename GCInfoType, typename CustomSpace>
struct AllocationDispatcher<GCInfoType, CustomSpace,
api_constants::kDefaultAlignment>
final {
static void* Invoke(AllocationHandle& handle, size_t size) {
static_assert(std::is_base_of<CustomSpaceBase, CustomSpace>::value,
"Custom space must inherit from CustomSpaceBase.");
return MakeGarbageCollectedTraitInternal::Allocate(
handle, size, internal::GCInfoTrait<GCInfoType>::Index(),
CustomSpace::kSpaceIndex);
}
};
private:
static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t,
GCInfoIndex);
static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&,
size_t, AlignVal,
GCInfoIndex);
static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t,
GCInfoIndex, CustomSpaceIndex);
static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&,
size_t, AlignVal, GCInfoIndex,
CustomSpaceIndex);
friend class HeapObjectHeader;
};
} // namespace internal
/**
* Base trait that provides utilities for advancers users that have custom
* allocation needs (e.g., overriding size). It's expected that users override
* MakeGarbageCollectedTrait (see below) and inherit from
* MakeGarbageCollectedTraitBase and make use of the low-level primitives
* offered to allocate and construct an object.
*/
template <typename T>
class MakeGarbageCollectedTraitBase
: private internal::MakeGarbageCollectedTraitInternal {
private:
static_assert(internal::IsGarbageCollectedType<T>::value,
"T needs to be a garbage collected object");
static_assert(!IsGarbageCollectedWithMixinTypeV<T> ||
sizeof(T) <=
internal::api_constants::kLargeObjectSizeThreshold,
"GarbageCollectedMixin may not be a large object");
protected:
/**
* Allocates memory for an object of type T.
*
* \param handle AllocationHandle identifying the heap to allocate the object
* on.
* \param size The size that should be reserved for the object.
* \returns the memory to construct an object of type T on.
*/
V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) {
static_assert(
std::is_base_of<typename T::ParentMostGarbageCollectedType, T>::value,
"U of GarbageCollected<U> must be a base of T. Check "
"GarbageCollected<T> base class inheritance.");
static constexpr size_t kWantedAlignment =
alignof(T) < internal::api_constants::kDefaultAlignment
? internal::api_constants::kDefaultAlignment
: alignof(T);
static_assert(
kWantedAlignment <= internal::api_constants::kMaxSupportedAlignment,
"Requested alignment larger than alignof(std::max_align_t) bytes. "
"Please file a bug to possibly get this restriction lifted.");
return AllocationDispatcher<
typename internal::GCInfoFolding<
T, typename T::ParentMostGarbageCollectedType>::ResultType,
typename SpaceTrait<T>::Space, kWantedAlignment>::Invoke(handle, size);
}
/**
* Marks an object as fully constructed, resulting in precise handling by the
* garbage collector.
*
* \param payload The base pointer the object is allocated at.
*/
V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) {
internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed(
payload);
}
};
/**
* Passed to MakeGarbageCollected to specify how many bytes should be appended
* to the allocated object.
*
* Example:
* \code
* class InlinedArray final : public GarbageCollected<InlinedArray> {
* public:
* explicit InlinedArray(size_t bytes) : size(bytes), byte_array(this + 1) {}
* void Trace(Visitor*) const {}
* size_t size;
* char* byte_array;
* };
*
* auto* inlined_array = MakeGarbageCollected<InlinedArray(
* GetAllocationHandle(), AdditionalBytes(4), 4);
* for (size_t i = 0; i < 4; i++) {
* Process(inlined_array->byte_array[i]);
* }
* \endcode
*/
struct AdditionalBytes {
constexpr explicit AdditionalBytes(size_t bytes) : value(bytes) {}
const size_t value;
};
/**
* Default trait class that specifies how to construct an object of type T.
* Advanced users may override how an object is constructed using the utilities
* that are provided through MakeGarbageCollectedTraitBase.
*
* Any trait overriding construction must
* - allocate through `MakeGarbageCollectedTraitBase<T>::Allocate`;
* - mark the object as fully constructed using
* `MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed`;
*/
template <typename T>
class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> {
public:
template <typename... Args>
static T* Call(AllocationHandle& handle, Args&&... args) {
void* memory =
MakeGarbageCollectedTraitBase<T>::Allocate(handle, sizeof(T));
T* object = ::new (memory) T(std::forward<Args>(args)...);
MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
return object;
}
template <typename... Args>
static T* Call(AllocationHandle& handle, AdditionalBytes additional_bytes,
Args&&... args) {
void* memory = MakeGarbageCollectedTraitBase<T>::Allocate(
handle, sizeof(T) + additional_bytes.value);
T* object = ::new (memory) T(std::forward<Args>(args)...);
MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
return object;
}
};
/**
* Allows users to specify a post-construction callback for specific types. The
* callback is invoked on the instance of type T right after it has been
* constructed. This can be useful when the callback requires a
* fully-constructed object to be able to dispatch to virtual methods.
*/
template <typename T, typename = void>
struct PostConstructionCallbackTrait {
static void Call(T*) {}
};
/**
* Constructs a managed object of type T where T transitively inherits from
* GarbageCollected.
*
* \param args List of arguments with which an instance of T will be
* constructed.
* \returns an instance of type T.
*/
template <typename T, typename... Args>
V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) {
T* object =
MakeGarbageCollectedTrait<T>::Call(handle, std::forward<Args>(args)...);
PostConstructionCallbackTrait<T>::Call(object);
return object;
}
/**
* Constructs a managed object of type T where T transitively inherits from
* GarbageCollected. Created objects will have additional bytes appended to
* it. Allocated memory would suffice for `sizeof(T) + additional_bytes`.
*
* \param additional_bytes Denotes how many bytes to append to T.
* \param args List of arguments with which an instance of T will be
* constructed.
* \returns an instance of type T.
*/
template <typename T, typename... Args>
V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle,
AdditionalBytes additional_bytes,
Args&&... args) {
T* object = MakeGarbageCollectedTrait<T>::Call(handle, additional_bytes,
std::forward<Args>(args)...);
PostConstructionCallbackTrait<T>::Call(object);
return object;
}
} // namespace cppgc
#undef CPPGC_DEFAULT_ALIGNED
#undef CPPGC_DOUBLE_WORD_ALIGNED
#endif // INCLUDE_CPPGC_ALLOCATION_H_

28
v8/include/cppgc/common.h Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_COMMON_H_
#define INCLUDE_CPPGC_COMMON_H_
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
/**
* Indicator for the stack state of the embedder.
*/
enum class EmbedderStackState {
/**
* Stack may contain interesting heap pointers.
*/
kMayContainHeapPointers,
/**
* Stack does not contain any interesting heap pointers.
*/
kNoHeapPointers,
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_COMMON_H_

View File

@ -0,0 +1,464 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
#include <atomic>
#include "cppgc/internal/persistent-node.h"
#include "cppgc/internal/pointer-policies.h"
#include "cppgc/persistent.h"
#include "cppgc/visitor.h"
namespace cppgc {
namespace internal {
// Wrapper around PersistentBase that allows accessing poisoned memory when
// using ASAN. This is needed as the GC of the heap that owns the value
// of a CTP, may clear it (heap termination, weakness) while the object
// holding the CTP may be poisoned as itself may be deemed dead.
class CrossThreadPersistentBase : public PersistentBase {
public:
CrossThreadPersistentBase() = default;
explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {}
V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const {
return raw_;
}
V8_CLANG_NO_SANITIZE("address")
PersistentNode* GetNodeFromGC() const { return node_; }
V8_CLANG_NO_SANITIZE("address")
void ClearFromGC() const {
raw_ = nullptr;
SetNodeSafe(nullptr);
}
// GetNodeSafe() can be used for a thread-safe IsValid() check in a
// double-checked locking pattern. See ~BasicCrossThreadPersistent.
PersistentNode* GetNodeSafe() const {
return reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->load(
std::memory_order_acquire);
}
// The GC writes using SetNodeSafe() while holding the lock.
V8_CLANG_NO_SANITIZE("address")
void SetNodeSafe(PersistentNode* value) const {
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define V8_IS_ASAN 1
#endif
#endif
#ifdef V8_IS_ASAN
__atomic_store(&node_, &value, __ATOMIC_RELEASE);
#else // !V8_IS_ASAN
// Non-ASAN builds can use atomics. This also covers MSVC which does not
// have the __atomic_store intrinsic.
reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->store(
value, std::memory_order_release);
#endif // !V8_IS_ASAN
#undef V8_IS_ASAN
}
};
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
class BasicCrossThreadPersistent final : public CrossThreadPersistentBase,
public LocationPolicy,
private WeaknessPolicy,
private CheckingPolicy {
public:
using typename WeaknessPolicy::IsStrongPersistent;
using PointeeType = T;
~BasicCrossThreadPersistent() {
// This implements fast path for destroying empty/sentinel.
//
// Simplified version of `AssignUnsafe()` to allow calling without a
// complete type `T`. Uses double-checked locking with a simple thread-safe
// check for a valid handle based on a node.
if (GetNodeSafe()) {
PersistentRegionLock guard;
const void* old_value = GetValue();
// The fast path check (GetNodeSafe()) does not acquire the lock. Recheck
// validity while holding the lock to ensure the reference has not been
// cleared.
if (IsValid(old_value)) {
CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
region.FreeNode(GetNode());
SetNode(nullptr);
} else {
CPPGC_DCHECK(!GetNode());
}
}
// No need to call SetValue() as the handle is not used anymore. This can
// leave behind stale sentinel values but will always destroy the underlying
// node.
}
BasicCrossThreadPersistent(
const SourceLocation& loc = SourceLocation::Current())
: LocationPolicy(loc) {}
BasicCrossThreadPersistent(
std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())
: LocationPolicy(loc) {}
BasicCrossThreadPersistent(
SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
: CrossThreadPersistentBase(s), LocationPolicy(loc) {}
BasicCrossThreadPersistent(
T* raw, const SourceLocation& loc = SourceLocation::Current())
: CrossThreadPersistentBase(raw), LocationPolicy(loc) {
if (!IsValid(raw)) return;
PersistentRegionLock guard;
CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
SetNode(region.AllocateNode(this, &TraceAsRoot));
this->CheckPointer(raw);
}
class UnsafeCtorTag {
private:
UnsafeCtorTag() = default;
template <typename U, typename OtherWeaknessPolicy,
typename OtherLocationPolicy, typename OtherCheckingPolicy>
friend class BasicCrossThreadPersistent;
};
BasicCrossThreadPersistent(
UnsafeCtorTag, T* raw,
const SourceLocation& loc = SourceLocation::Current())
: CrossThreadPersistentBase(raw), LocationPolicy(loc) {
if (!IsValid(raw)) return;
CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
SetNode(region.AllocateNode(this, &TraceAsRoot));
this->CheckPointer(raw);
}
BasicCrossThreadPersistent(
T& raw, const SourceLocation& loc = SourceLocation::Current())
: BasicCrossThreadPersistent(&raw, loc) {}
template <typename U, typename MemberBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicCrossThreadPersistent(
internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
MemberCheckingPolicy>
member,
const SourceLocation& loc = SourceLocation::Current())
: BasicCrossThreadPersistent(member.Get(), loc) {}
BasicCrossThreadPersistent(
const BasicCrossThreadPersistent& other,
const SourceLocation& loc = SourceLocation::Current())
: BasicCrossThreadPersistent(loc) {
// Invoke operator=.
*this = other;
}
// Heterogeneous ctor.
template <typename U, typename OtherWeaknessPolicy,
typename OtherLocationPolicy, typename OtherCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicCrossThreadPersistent(
const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
OtherLocationPolicy,
OtherCheckingPolicy>& other,
const SourceLocation& loc = SourceLocation::Current())
: BasicCrossThreadPersistent(loc) {
*this = other;
}
BasicCrossThreadPersistent(
BasicCrossThreadPersistent&& other,
const SourceLocation& loc = SourceLocation::Current()) noexcept {
// Invoke operator=.
*this = std::move(other);
}
BasicCrossThreadPersistent& operator=(
const BasicCrossThreadPersistent& other) {
PersistentRegionLock guard;
AssignSafe(guard, other.Get());
return *this;
}
template <typename U, typename OtherWeaknessPolicy,
typename OtherLocationPolicy, typename OtherCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicCrossThreadPersistent& operator=(
const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
OtherLocationPolicy,
OtherCheckingPolicy>& other) {
PersistentRegionLock guard;
AssignSafe(guard, other.Get());
return *this;
}
BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) {
if (this == &other) return *this;
Clear();
PersistentRegionLock guard;
PersistentBase::operator=(std::move(other));
LocationPolicy::operator=(std::move(other));
if (!IsValid(GetValue())) return *this;
GetNode()->UpdateOwner(this);
other.SetValue(nullptr);
other.SetNode(nullptr);
this->CheckPointer(Get());
return *this;
}
/**
* Assigns a raw pointer.
*
* Note: **Not thread-safe.**
*/
BasicCrossThreadPersistent& operator=(T* other) {
AssignUnsafe(other);
return *this;
}
// Assignment from member.
template <typename U, typename MemberBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicCrossThreadPersistent& operator=(
internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
MemberCheckingPolicy>
member) {
return operator=(member.Get());
}
/**
* Assigns a nullptr.
*
* \returns the handle.
*/
BasicCrossThreadPersistent& operator=(std::nullptr_t) {
Clear();
return *this;
}
/**
* Assigns the sentinel pointer.
*
* \returns the handle.
*/
BasicCrossThreadPersistent& operator=(SentinelPointer s) {
PersistentRegionLock guard;
AssignSafe(guard, s);
return *this;
}
/**
* Returns a pointer to the stored object.
*
* Note: **Not thread-safe.**
*
* \returns a pointer to the stored object.
*/
// CFI cast exemption to allow passing SentinelPointer through T* and support
// heterogeneous assignments between different Member and Persistent handles
// based on their actual types.
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
return static_cast<T*>(const_cast<void*>(GetValue()));
}
/**
* Clears the stored object.
*/
void Clear() {
PersistentRegionLock guard;
AssignSafe(guard, nullptr);
}
/**
* Returns a pointer to the stored object and releases it.
*
* Note: **Not thread-safe.**
*
* \returns a pointer to the stored object.
*/
T* Release() {
T* result = Get();
Clear();
return result;
}
/**
* Conversio to boolean.
*
* Note: **Not thread-safe.**
*
* \returns true if an actual object has been stored and false otherwise.
*/
explicit operator bool() const { return Get(); }
/**
* Conversion to object of type T.
*
* Note: **Not thread-safe.**
*
* \returns the object.
*/
operator T*() const { return Get(); }
/**
* Dereferences the stored object.
*
* Note: **Not thread-safe.**
*/
T* operator->() const { return Get(); }
T& operator*() const { return *Get(); }
template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
typename OtherLocationPolicy = LocationPolicy,
typename OtherCheckingPolicy = CheckingPolicy>
BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>
To() const {
using OtherBasicCrossThreadPersistent =
BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>;
PersistentRegionLock guard;
return OtherBasicCrossThreadPersistent(
typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
static_cast<U*>(Get()));
}
template <typename U = T,
typename = typename std::enable_if<!BasicCrossThreadPersistent<
U, WeaknessPolicy>::IsStrongPersistent::value>::type>
BasicCrossThreadPersistent<U, internal::StrongCrossThreadPersistentPolicy>
Lock() const {
return BasicCrossThreadPersistent<
U, internal::StrongCrossThreadPersistentPolicy>(*this);
}
private:
static bool IsValid(const void* ptr) {
return ptr && ptr != kSentinelPointer;
}
static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr));
}
void AssignUnsafe(T* ptr) {
const void* old_value = GetValue();
if (IsValid(old_value)) {
PersistentRegionLock guard;
old_value = GetValue();
// The fast path check (IsValid()) does not acquire the lock. Reload
// the value to ensure the reference has not been cleared.
if (IsValid(old_value)) {
CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
SetValue(ptr);
this->CheckPointer(ptr);
return;
}
region.FreeNode(GetNode());
SetNode(nullptr);
} else {
CPPGC_DCHECK(!GetNode());
}
}
SetValue(ptr);
if (!IsValid(ptr)) return;
PersistentRegionLock guard;
SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
this->CheckPointer(ptr);
}
void AssignSafe(PersistentRegionLock&, T* ptr) {
PersistentRegionLock::AssertLocked();
const void* old_value = GetValue();
if (IsValid(old_value)) {
CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
SetValue(ptr);
this->CheckPointer(ptr);
return;
}
region.FreeNode(GetNode());
SetNode(nullptr);
}
SetValue(ptr);
if (!IsValid(ptr)) return;
SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
this->CheckPointer(ptr);
}
void ClearFromGC() const {
if (IsValid(GetValueFromGC())) {
WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
.FreeNode(GetNodeFromGC());
CrossThreadPersistentBase::ClearFromGC();
}
}
// See Get() for details.
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
T* GetFromGC() const {
return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
}
friend class internal::RootVisitor;
};
template <typename T, typename LocationPolicy, typename CheckingPolicy>
struct IsWeak<
BasicCrossThreadPersistent<T, internal::WeakCrossThreadPersistentPolicy,
LocationPolicy, CheckingPolicy>>
: std::true_type {};
} // namespace internal
namespace subtle {
/**
* **DO NOT USE: Has known caveats, see below.**
*
* CrossThreadPersistent allows retaining objects from threads other than the
* thread the owning heap is operating on.
*
* Known caveats:
* - Does not protect the heap owning an object from terminating.
* - Reaching transitively through the graph is unsupported as objects may be
* moved concurrently on the thread owning the object.
*/
template <typename T>
using CrossThreadPersistent = internal::BasicCrossThreadPersistent<
T, internal::StrongCrossThreadPersistentPolicy>;
/**
* **DO NOT USE: Has known caveats, see below.**
*
* CrossThreadPersistent allows weakly retaining objects from threads other than
* the thread the owning heap is operating on.
*
* Known caveats:
* - Does not protect the heap owning an object from terminating.
* - Reaching transitively through the graph is unsupported as objects may be
* moved concurrently on the thread owning the object.
*/
template <typename T>
using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent<
T, internal::WeakCrossThreadPersistentPolicy>;
} // namespace subtle
} // namespace cppgc
#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_

View File

@ -0,0 +1,97 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_CUSTOM_SPACE_H_
#define INCLUDE_CPPGC_CUSTOM_SPACE_H_
#include <stddef.h>
namespace cppgc {
/**
* Index identifying a custom space.
*/
struct CustomSpaceIndex {
constexpr CustomSpaceIndex(size_t value) : value(value) {} // NOLINT
size_t value;
};
/**
* Top-level base class for custom spaces. Users must inherit from CustomSpace
* below.
*/
class CustomSpaceBase {
public:
virtual ~CustomSpaceBase() = default;
virtual CustomSpaceIndex GetCustomSpaceIndex() const = 0;
virtual bool IsCompactable() const = 0;
};
/**
* Base class custom spaces should directly inherit from. The class inheriting
* from `CustomSpace` must define `kSpaceIndex` as unique space index. These
* indices need for form a sequence starting at 0.
*
* Example:
* \code
* class CustomSpace1 : public CustomSpace<CustomSpace1> {
* public:
* static constexpr CustomSpaceIndex kSpaceIndex = 0;
* };
* class CustomSpace2 : public CustomSpace<CustomSpace2> {
* public:
* static constexpr CustomSpaceIndex kSpaceIndex = 1;
* };
* \endcode
*/
template <typename ConcreteCustomSpace>
class CustomSpace : public CustomSpaceBase {
public:
/**
* Compaction is only supported on spaces that manually manage slots
* recording.
*/
static constexpr bool kSupportsCompaction = false;
CustomSpaceIndex GetCustomSpaceIndex() const final {
return ConcreteCustomSpace::kSpaceIndex;
}
bool IsCompactable() const final {
return ConcreteCustomSpace::kSupportsCompaction;
}
};
/**
* User-overridable trait that allows pinning types to custom spaces.
*/
template <typename T, typename = void>
struct SpaceTrait {
using Space = void;
};
namespace internal {
template <typename CustomSpace>
struct IsAllocatedOnCompactableSpaceImpl {
static constexpr bool value = CustomSpace::kSupportsCompaction;
};
template <>
struct IsAllocatedOnCompactableSpaceImpl<void> {
// Non-custom spaces are by default not compactable.
static constexpr bool value = false;
};
template <typename T>
struct IsAllocatedOnCompactableSpace {
public:
static constexpr bool value =
IsAllocatedOnCompactableSpaceImpl<typename SpaceTrait<T>::Space>::value;
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_CUSTOM_SPACE_H_

View File

@ -0,0 +1,67 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_DEFAULT_PLATFORM_H_
#define INCLUDE_CPPGC_DEFAULT_PLATFORM_H_
#include <memory>
#include "cppgc/platform.h"
#include "libplatform/libplatform.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
/**
* Platform provided by cppgc. Uses V8's DefaultPlatform provided by
* libplatform internally. Exception: `GetForegroundTaskRunner()`, see below.
*/
class V8_EXPORT DefaultPlatform : public Platform {
public:
using IdleTaskSupport = v8::platform::IdleTaskSupport;
explicit DefaultPlatform(
int thread_pool_size = 0,
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
std::unique_ptr<TracingController> tracing_controller = {})
: v8_platform_(v8::platform::NewDefaultPlatform(
thread_pool_size, idle_task_support,
v8::platform::InProcessStackDumping::kDisabled,
std::move(tracing_controller))) {}
cppgc::PageAllocator* GetPageAllocator() override {
return v8_platform_->GetPageAllocator();
}
double MonotonicallyIncreasingTime() override {
return v8_platform_->MonotonicallyIncreasingTime();
}
std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
// V8's default platform creates a new task runner when passed the
// `v8::Isolate` pointer the first time. For non-default platforms this will
// require getting the appropriate task runner.
return v8_platform_->GetForegroundTaskRunner(kNoIsolate);
}
std::unique_ptr<cppgc::JobHandle> PostJob(
cppgc::TaskPriority priority,
std::unique_ptr<cppgc::JobTask> job_task) override {
return v8_platform_->PostJob(priority, std::move(job_task));
}
TracingController* GetTracingController() override {
return v8_platform_->GetTracingController();
}
v8::Platform* GetV8Platform() const { return v8_platform_.get(); }
protected:
static constexpr v8::Isolate* kNoIsolate = nullptr;
std::unique_ptr<v8::Platform> v8_platform_;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_DEFAULT_PLATFORM_H_

View File

@ -0,0 +1,30 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_EPHEMERON_PAIR_H_
#define INCLUDE_CPPGC_EPHEMERON_PAIR_H_
#include "cppgc/liveness-broker.h"
#include "cppgc/member.h"
namespace cppgc {
/**
* An ephemeron pair is used to conditionally retain an object.
* The `value` will be kept alive only if the `key` is alive.
*/
template <typename K, typename V>
struct EphemeronPair {
EphemeronPair(K* k, V* v) : key(k), value(v) {}
WeakMember<K> key;
Member<V> value;
void ClearValueIfKeyIsDead(const LivenessBroker& broker) {
if (!broker.IsHeapObjectAlive(key)) value = nullptr;
}
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_EPHEMERON_PAIR_H_

View File

@ -0,0 +1,100 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_
#define INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_
#include <cstddef>
#include "cppgc/allocation.h"
#include "cppgc/internal/logging.h"
#include "cppgc/type-traits.h"
namespace cppgc {
class HeapHandle;
namespace subtle {
template <typename T>
void FreeUnreferencedObject(HeapHandle& heap_handle, T& object);
template <typename T>
bool Resize(T& object, AdditionalBytes additional_bytes);
} // namespace subtle
namespace internal {
class ExplicitManagementImpl final {
private:
V8_EXPORT static void FreeUnreferencedObject(HeapHandle&, void*);
V8_EXPORT static bool Resize(void*, size_t);
template <typename T>
friend void subtle::FreeUnreferencedObject(HeapHandle&, T&);
template <typename T>
friend bool subtle::Resize(T&, AdditionalBytes);
};
} // namespace internal
namespace subtle {
/**
* Informs the garbage collector that `object` can be immediately reclaimed. The
* destructor may not be invoked immediately but only on next garbage
* collection.
*
* It is up to the embedder to guarantee that no other object holds a reference
* to `object` after calling `FreeUnreferencedObject()`. In case such a
* reference exists, it's use results in a use-after-free.
*
* To aid in using the API, `FreeUnreferencedObject()` may be called from
* destructors on objects that would be reclaimed in the same garbage collection
* cycle.
*
* \param heap_handle The corresponding heap.
* \param object Reference to an object that is of type `GarbageCollected` and
* should be immediately reclaimed.
*/
template <typename T>
void FreeUnreferencedObject(HeapHandle& heap_handle, T& object) {
static_assert(IsGarbageCollectedTypeV<T>,
"Object must be of type GarbageCollected.");
internal::ExplicitManagementImpl::FreeUnreferencedObject(heap_handle,
&object);
}
/**
* Tries to resize `object` of type `T` with additional bytes on top of
* sizeof(T). Resizing is only useful with trailing inlined storage, see e.g.
* `MakeGarbageCollected(AllocationHandle&, AdditionalBytes)`.
*
* `Resize()` performs growing or shrinking as needed and may skip the operation
* for internal reasons, see return value.
*
* It is up to the embedder to guarantee that in case of shrinking a larger
* object down, the reclaimed area is not used anymore. Any subsequent use
* results in a use-after-free.
*
* The `object` must be live when calling `Resize()`.
*
* \param object Reference to an object that is of type `GarbageCollected` and
* should be resized.
* \param additional_bytes Bytes in addition to sizeof(T) that the object should
* provide.
* \returns true when the operation was successful and the result can be relied
* on, and false otherwise.
*/
template <typename T>
bool Resize(T& object, AdditionalBytes additional_bytes) {
static_assert(IsGarbageCollectedTypeV<T>,
"Object must be of type GarbageCollected.");
return internal::ExplicitManagementImpl::Resize(
&object, sizeof(T) + additional_bytes.value);
}
} // namespace subtle
} // namespace cppgc
#endif // INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_

View File

@ -0,0 +1,106 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
#define INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
#include "cppgc/internal/api-constants.h"
#include "cppgc/platform.h"
#include "cppgc/trace-trait.h"
#include "cppgc/type-traits.h"
namespace cppgc {
class Visitor;
/**
* Base class for managed objects. Only descendent types of `GarbageCollected`
* can be constructed using `MakeGarbageCollected()`. Must be inherited from as
* left-most base class.
*
* Types inheriting from GarbageCollected must provide a method of
* signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed
* pointers to the visitor and delegates to garbage-collected base classes.
* The method must be virtual if the type is not directly a child of
* GarbageCollected and marked as final.
*
* \code
* // Example using final class.
* class FinalType final : public GarbageCollected<FinalType> {
* public:
* void Trace(cppgc::Visitor* visitor) const {
* // Dispatch using visitor->Trace(...);
* }
* };
*
* // Example using non-final base class.
* class NonFinalBase : public GarbageCollected<NonFinalBase> {
* public:
* virtual void Trace(cppgc::Visitor*) const {}
* };
*
* class FinalChild final : public NonFinalBase {
* public:
* void Trace(cppgc::Visitor* visitor) const final {
* // Dispatch using visitor->Trace(...);
* NonFinalBase::Trace(visitor);
* }
* };
* \endcode
*/
template <typename T>
class GarbageCollected {
public:
using IsGarbageCollectedTypeMarker = void;
using ParentMostGarbageCollectedType = T;
// Must use MakeGarbageCollected.
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
// The garbage collector is taking care of reclaiming the object. Also,
// virtual destructor requires an unambiguous, accessible 'operator delete'.
void operator delete(void*) {
#ifdef V8_ENABLE_CHECKS
internal::Fatal(
"Manually deleting a garbage collected object is not allowed");
#endif // V8_ENABLE_CHECKS
}
void operator delete[](void*) = delete;
protected:
GarbageCollected() = default;
};
/**
* Base class for managed mixin objects. Such objects cannot be constructed
* directly but must be mixed into the inheritance hierarchy of a
* GarbageCollected object.
*
* Types inheriting from GarbageCollectedMixin must override a virtual method
* of signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed
* pointers to the visitor and delegates to base classes.
*
* \code
* class Mixin : public GarbageCollectedMixin {
* public:
* void Trace(cppgc::Visitor* visitor) const override {
* // Dispatch using visitor->Trace(...);
* }
* };
* \endcode
*/
class GarbageCollectedMixin {
public:
using IsGarbageCollectedMixinTypeMarker = void;
/**
* This Trace method must be overriden by objects inheriting from
* GarbageCollectedMixin.
*/
virtual void Trace(cppgc::Visitor*) const {}
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_

View File

@ -0,0 +1,309 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_HEAP_CONSISTENCY_H_
#define INCLUDE_CPPGC_HEAP_CONSISTENCY_H_
#include <cstddef>
#include "cppgc/internal/write-barrier.h"
#include "cppgc/macros.h"
#include "cppgc/member.h"
#include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
class HeapHandle;
namespace subtle {
/**
* **DO NOT USE: Use the appropriate managed types.**
*
* Consistency helpers that aid in maintaining a consistent internal state of
* the garbage collector.
*/
class HeapConsistency final {
public:
using WriteBarrierParams = internal::WriteBarrier::Params;
using WriteBarrierType = internal::WriteBarrier::Type;
/**
* Gets the required write barrier type for a specific write.
*
* \param slot Slot containing the pointer to the object. The slot itself
* must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
* \param value The pointer to the object. May be an interior pointer to an
* interface of the actual object.
* \param params Parameters that may be used for actual write barrier calls.
* Only filled if return value indicates that a write barrier is needed. The
* contents of the `params` are an implementation detail.
* \returns whether a write barrier is needed and which barrier to invoke.
*/
static V8_INLINE WriteBarrierType GetWriteBarrierType(
const void* slot, const void* value, WriteBarrierParams& params) {
return internal::WriteBarrier::GetWriteBarrierType(slot, value, params);
}
/**
* Gets the required write barrier type for a specific write. This override is
* only used for all the BasicMember types.
*
* \param slot Slot containing the pointer to the object. The slot itself
* must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
* \param value The pointer to the object held via `BasicMember`.
* \param params Parameters that may be used for actual write barrier calls.
* Only filled if return value indicates that a write barrier is needed. The
* contents of the `params` are an implementation detail.
* \returns whether a write barrier is needed and which barrier to invoke.
*/
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
static V8_INLINE WriteBarrierType GetWriteBarrierType(
const internal::BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& value,
WriteBarrierParams& params) {
return internal::WriteBarrier::GetWriteBarrierType(
value.GetRawSlot(), value.GetRawStorage(), params);
}
/**
* Gets the required write barrier type for a specific write.
*
* \param slot Slot to some part of an object. The object must not necessarily
have been allocated using `MakeGarbageCollected()` but can also live
off-heap or on stack.
* \param params Parameters that may be used for actual write barrier calls.
* Only filled if return value indicates that a write barrier is needed. The
* contents of the `params` are an implementation detail.
* \param callback Callback returning the corresponding heap handle. The
* callback is only invoked if the heap cannot otherwise be figured out. The
* callback must not allocate.
* \returns whether a write barrier is needed and which barrier to invoke.
*/
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrierType
GetWriteBarrierType(const void* slot, WriteBarrierParams& params,
HeapHandleCallback callback) {
return internal::WriteBarrier::GetWriteBarrierType(slot, params, callback);
}
/**
* Gets the required write barrier type for a specific write.
* This version is meant to be used in conjunction with with a marking write
* barrier barrier which doesn't consider the slot.
*
* \param value The pointer to the object. May be an interior pointer to an
* interface of the actual object.
* \param params Parameters that may be used for actual write barrier calls.
* Only filled if return value indicates that a write barrier is needed. The
* contents of the `params` are an implementation detail.
* \returns whether a write barrier is needed and which barrier to invoke.
*/
static V8_INLINE WriteBarrierType
GetWriteBarrierType(const void* value, WriteBarrierParams& params) {
return internal::WriteBarrier::GetWriteBarrierType(value, params);
}
/**
* Conservative Dijkstra-style write barrier that processes an object if it
* has not yet been processed.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param object The pointer to the object. May be an interior pointer to a
* an interface of the actual object.
*/
static V8_INLINE void DijkstraWriteBarrier(const WriteBarrierParams& params,
const void* object) {
internal::WriteBarrier::DijkstraMarkingBarrier(params, object);
}
/**
* Conservative Dijkstra-style write barrier that processes a range of
* elements if they have not yet been processed.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param first_element Pointer to the first element that should be processed.
* The slot itself must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
* \param element_size Size of the element in bytes.
* \param number_of_elements Number of elements that should be processed,
* starting with `first_element`.
* \param trace_callback The trace callback that should be invoked for each
* element if necessary.
*/
static V8_INLINE void DijkstraWriteBarrierRange(
const WriteBarrierParams& params, const void* first_element,
size_t element_size, size_t number_of_elements,
TraceCallback trace_callback) {
internal::WriteBarrier::DijkstraMarkingBarrierRange(
params, first_element, element_size, number_of_elements,
trace_callback);
}
/**
* Steele-style write barrier that re-processes an object if it has already
* been processed.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param object The pointer to the object which must point to an object that
* has been allocated using `MakeGarbageCollected()`. Interior pointers are
* not supported.
*/
static V8_INLINE void SteeleWriteBarrier(const WriteBarrierParams& params,
const void* object) {
internal::WriteBarrier::SteeleMarkingBarrier(params, object);
}
/**
* Generational barrier for maintaining consistency when running with multiple
* generations.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param slot Slot containing the pointer to the object. The slot itself
* must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
*/
static V8_INLINE void GenerationalBarrier(const WriteBarrierParams& params,
const void* slot) {
internal::WriteBarrier::GenerationalBarrier<
internal::WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params,
slot);
}
/**
* Generational barrier for maintaining consistency when running with multiple
* generations. This version is used when slot contains uncompressed pointer.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param slot Uncompressed slot containing the direct pointer to the object.
* The slot itself must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
*/
static V8_INLINE void GenerationalBarrierForUncompressedSlot(
const WriteBarrierParams& params, const void* uncompressed_slot) {
internal::WriteBarrier::GenerationalBarrier<
internal::WriteBarrier::GenerationalBarrierType::
kPreciseUncompressedSlot>(params, uncompressed_slot);
}
/**
* Generational barrier for source object that may contain outgoing pointers
* to objects in young generation.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param inner_pointer Pointer to the source object.
*/
static V8_INLINE void GenerationalBarrierForSourceObject(
const WriteBarrierParams& params, const void* inner_pointer) {
internal::WriteBarrier::GenerationalBarrier<
internal::WriteBarrier::GenerationalBarrierType::kImpreciseSlot>(
params, inner_pointer);
}
private:
HeapConsistency() = delete;
};
/**
* Disallows garbage collection finalizations. Any garbage collection triggers
* result in a crash when in this scope.
*
* Note that the garbage collector already covers paths that can lead to garbage
* collections, so user code does not require checking
* `IsGarbageCollectionAllowed()` before allocations.
*/
class V8_EXPORT V8_NODISCARD DisallowGarbageCollectionScope final {
CPPGC_STACK_ALLOCATED();
public:
/**
* \returns whether garbage collections are currently allowed.
*/
static bool IsGarbageCollectionAllowed(HeapHandle& heap_handle);
/**
* Enters a disallow garbage collection scope. Must be paired with `Leave()`.
* Prefer a scope instance of `DisallowGarbageCollectionScope`.
*
* \param heap_handle The corresponding heap.
*/
static void Enter(HeapHandle& heap_handle);
/**
* Leaves a disallow garbage collection scope. Must be paired with `Enter()`.
* Prefer a scope instance of `DisallowGarbageCollectionScope`.
*
* \param heap_handle The corresponding heap.
*/
static void Leave(HeapHandle& heap_handle);
/**
* Constructs a scoped object that automatically enters and leaves a disallow
* garbage collection scope based on its lifetime.
*
* \param heap_handle The corresponding heap.
*/
explicit DisallowGarbageCollectionScope(HeapHandle& heap_handle);
~DisallowGarbageCollectionScope();
DisallowGarbageCollectionScope(const DisallowGarbageCollectionScope&) =
delete;
DisallowGarbageCollectionScope& operator=(
const DisallowGarbageCollectionScope&) = delete;
private:
HeapHandle& heap_handle_;
};
/**
* Avoids invoking garbage collection finalizations. Already running garbage
* collection phase are unaffected by this scope.
*
* Should only be used temporarily as the scope has an impact on memory usage
* and follow up garbage collections.
*/
class V8_EXPORT V8_NODISCARD NoGarbageCollectionScope final {
CPPGC_STACK_ALLOCATED();
public:
/**
* Enters a no garbage collection scope. Must be paired with `Leave()`. Prefer
* a scope instance of `NoGarbageCollectionScope`.
*
* \param heap_handle The corresponding heap.
*/
static void Enter(HeapHandle& heap_handle);
/**
* Leaves a no garbage collection scope. Must be paired with `Enter()`. Prefer
* a scope instance of `NoGarbageCollectionScope`.
*
* \param heap_handle The corresponding heap.
*/
static void Leave(HeapHandle& heap_handle);
/**
* Constructs a scoped object that automatically enters and leaves a no
* garbage collection scope based on its lifetime.
*
* \param heap_handle The corresponding heap.
*/
explicit NoGarbageCollectionScope(HeapHandle& heap_handle);
~NoGarbageCollectionScope();
NoGarbageCollectionScope(const NoGarbageCollectionScope&) = delete;
NoGarbageCollectionScope& operator=(const NoGarbageCollectionScope&) = delete;
private:
HeapHandle& heap_handle_;
};
} // namespace subtle
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_CONSISTENCY_H_

View File

@ -0,0 +1,43 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_HEAP_HANDLE_H_
#define INCLUDE_CPPGC_HEAP_HANDLE_H_
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class HeapBase;
class WriteBarrierTypeForCagedHeapPolicy;
class WriteBarrierTypeForNonCagedHeapPolicy;
} // namespace internal
/**
* Opaque handle used for additional heap APIs.
*/
class HeapHandle {
private:
HeapHandle() = default;
V8_INLINE bool is_incremental_marking_in_progress() const {
return is_incremental_marking_in_progress_;
}
V8_INLINE bool is_young_generation_enabled() const {
return is_young_generation_enabled_;
}
bool is_incremental_marking_in_progress_ = false;
bool is_young_generation_enabled_ = false;
friend class internal::HeapBase;
friend class internal::WriteBarrierTypeForCagedHeapPolicy;
friend class internal::WriteBarrierTypeForNonCagedHeapPolicy;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_HANDLE_H_

View File

@ -0,0 +1,82 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_HEAP_STATE_H_
#define INCLUDE_CPPGC_HEAP_STATE_H_
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
class HeapHandle;
namespace subtle {
/**
* Helpers to peek into heap-internal state.
*/
class V8_EXPORT HeapState final {
public:
/**
* Returns whether the garbage collector is marking. This API is experimental
* and is expected to be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently marking, and false
* otherwise.
*/
static bool IsMarking(const HeapHandle& heap_handle);
/*
* Returns whether the garbage collector is sweeping. This API is experimental
* and is expected to be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently sweeping, and false
* otherwise.
*/
static bool IsSweeping(const HeapHandle& heap_handle);
/*
* Returns whether the garbage collector is currently sweeping on the thread
* owning this heap. This API allows the caller to determine whether it has
* been called from a destructor of a managed object. This API is experimental
* and may be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently sweeping on this
* thread, and false otherwise.
*/
static bool IsSweepingOnOwningThread(const HeapHandle& heap_handle);
/**
* Returns whether the garbage collector is in the atomic pause, i.e., the
* mutator is stopped from running. This API is experimental and is expected
* to be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the garbage collector is currently in the atomic pause,
* and false otherwise.
*/
static bool IsInAtomicPause(const HeapHandle& heap_handle);
/**
* Returns whether the last garbage collection was finalized conservatively
* (i.e., with a non-empty stack). This API is experimental and is expected to
* be removed in future.
*
* \param heap_handle The corresponding heap.
* \returns true if the last garbage collection was finalized conservatively,
* and false otherwise.
*/
static bool PreviousGCWasConservative(const HeapHandle& heap_handle);
private:
HeapState() = delete;
};
} // namespace subtle
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_STATE_H_

View File

@ -0,0 +1,120 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_HEAP_STATISTICS_H_
#define INCLUDE_CPPGC_HEAP_STATISTICS_H_
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace cppgc {
/**
* `HeapStatistics` contains memory consumption and utilization statistics for a
* cppgc heap.
*/
struct HeapStatistics final {
/**
* Specifies the detail level of the heap statistics. Brief statistics contain
* only the top-level allocated and used memory statistics for the entire
* heap. Detailed statistics also contain a break down per space and page, as
* well as freelist statistics and object type histograms. Note that used
* memory reported by brief statistics and detailed statistics might differ
* slightly.
*/
enum DetailLevel : uint8_t {
kBrief,
kDetailed,
};
/**
* Object statistics for a single type.
*/
struct ObjectStatsEntry {
/**
* Number of allocated bytes.
*/
size_t allocated_bytes;
/**
* Number of allocated objects.
*/
size_t object_count;
};
/**
* Page granularity statistics. For each page the statistics record the
* allocated memory size and overall used memory size for the page.
*/
struct PageStatistics {
/** Overall committed amount of memory for the page. */
size_t committed_size_bytes = 0;
/** Resident amount of memory held by the page. */
size_t resident_size_bytes = 0;
/** Amount of memory actually used on the page. */
size_t used_size_bytes = 0;
/** Statistics for object allocated on the page. Filled only when
* NameProvider::SupportsCppClassNamesAsObjectNames() is true. */
std::vector<ObjectStatsEntry> object_statistics;
};
/**
* Statistics of the freelist (used only in non-large object spaces). For
* each bucket in the freelist the statistics record the bucket size, the
* number of freelist entries in the bucket, and the overall allocated memory
* consumed by these freelist entries.
*/
struct FreeListStatistics {
/** bucket sizes in the freelist. */
std::vector<size_t> bucket_size;
/** number of freelist entries per bucket. */
std::vector<size_t> free_count;
/** memory size consumed by freelist entries per size. */
std::vector<size_t> free_size;
};
/**
* Space granularity statistics. For each space the statistics record the
* space name, the amount of allocated memory and overall used memory for the
* space. The statistics also contain statistics for each of the space's
* pages, its freelist and the objects allocated on the space.
*/
struct SpaceStatistics {
/** The space name */
std::string name;
/** Overall committed amount of memory for the heap. */
size_t committed_size_bytes = 0;
/** Resident amount of memory held by the heap. */
size_t resident_size_bytes = 0;
/** Amount of memory actually used on the space. */
size_t used_size_bytes = 0;
/** Statistics for each of the pages in the space. */
std::vector<PageStatistics> page_stats;
/** Statistics for the freelist of the space. */
FreeListStatistics free_list_stats;
};
/** Overall committed amount of memory for the heap. */
size_t committed_size_bytes = 0;
/** Resident amount of memory held by the heap. */
size_t resident_size_bytes = 0;
/** Amount of memory actually used on the heap. */
size_t used_size_bytes = 0;
/** Detail level of this HeapStatistics. */
DetailLevel detail_level;
/** Statistics for each of the spaces in the heap. Filled only when
* `detail_level` is `DetailLevel::kDetailed`. */
std::vector<SpaceStatistics> space_stats;
/**
* Vector of `cppgc::GarbageCollected` type names.
*/
std::vector<std::string> type_names;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_STATISTICS_H_

202
v8/include/cppgc/heap.h Normal file
View File

@ -0,0 +1,202 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_HEAP_H_
#define INCLUDE_CPPGC_HEAP_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include "cppgc/common.h"
#include "cppgc/custom-space.h"
#include "cppgc/platform.h"
#include "v8config.h" // NOLINT(build/include_directory)
/**
* cppgc - A C++ garbage collection library.
*/
namespace cppgc {
class AllocationHandle;
class HeapHandle;
/**
* Implementation details of cppgc. Those details are considered internal and
* may change at any point in time without notice. Users should never rely on
* the contents of this namespace.
*/
namespace internal {
class Heap;
} // namespace internal
class V8_EXPORT Heap {
public:
/**
* Specifies the stack state the embedder is in.
*/
using StackState = EmbedderStackState;
/**
* Specifies whether conservative stack scanning is supported.
*/
enum class StackSupport : uint8_t {
/**
* Conservative stack scan is supported.
*/
kSupportsConservativeStackScan,
/**
* Conservative stack scan is not supported. Embedders may use this option
* when using custom infrastructure that is unsupported by the library.
*/
kNoConservativeStackScan,
};
/**
* Specifies supported marking types.
*/
enum class MarkingType : uint8_t {
/**
* Atomic stop-the-world marking. This option does not require any write
* barriers but is the most intrusive in terms of jank.
*/
kAtomic,
/**
* Incremental marking interleaves marking with the rest of the application
* workload on the same thread.
*/
kIncremental,
/**
* Incremental and concurrent marking.
*/
kIncrementalAndConcurrent
};
/**
* Specifies supported sweeping types.
*/
enum class SweepingType : uint8_t {
/**
* Atomic stop-the-world sweeping. All of sweeping is performed at once.
*/
kAtomic,
/**
* Incremental sweeping interleaves sweeping with the rest of the
* application workload on the same thread.
*/
kIncremental,
/**
* Incremental and concurrent sweeping. Sweeping is split and interleaved
* with the rest of the application.
*/
kIncrementalAndConcurrent
};
/**
* Constraints for a Heap setup.
*/
struct ResourceConstraints {
/**
* Allows the heap to grow to some initial size in bytes before triggering
* garbage collections. This is useful when it is known that applications
* need a certain minimum heap to run to avoid repeatedly invoking the
* garbage collector when growing the heap.
*/
size_t initial_heap_size_bytes = 0;
};
/**
* Options specifying Heap properties (e.g. custom spaces) when initializing a
* heap through `Heap::Create()`.
*/
struct HeapOptions {
/**
* Creates reasonable defaults for instantiating a Heap.
*
* \returns the HeapOptions that can be passed to `Heap::Create()`.
*/
static HeapOptions Default() { return {}; }
/**
* Custom spaces added to heap are required to have indices forming a
* numbered sequence starting at 0, i.e., their `kSpaceIndex` must
* correspond to the index they reside in the vector.
*/
std::vector<std::unique_ptr<CustomSpaceBase>> custom_spaces;
/**
* Specifies whether conservative stack scan is supported. When conservative
* stack scan is not supported, the collector may try to invoke
* garbage collections using non-nestable task, which are guaranteed to have
* no interesting stack, through the provided Platform. If such tasks are
* not supported by the Platform, the embedder must take care of invoking
* the GC through `ForceGarbageCollectionSlow()`.
*/
StackSupport stack_support = StackSupport::kSupportsConservativeStackScan;
/**
* Specifies which types of marking are supported by the heap.
*/
MarkingType marking_support = MarkingType::kIncrementalAndConcurrent;
/**
* Specifies which types of sweeping are supported by the heap.
*/
SweepingType sweeping_support = SweepingType::kIncrementalAndConcurrent;
/**
* Resource constraints specifying various properties that the internal
* GC scheduler follows.
*/
ResourceConstraints resource_constraints;
};
/**
* Creates a new heap that can be used for object allocation.
*
* \param platform implemented and provided by the embedder.
* \param options HeapOptions specifying various properties for the Heap.
* \returns a new Heap instance.
*/
static std::unique_ptr<Heap> Create(
std::shared_ptr<Platform> platform,
HeapOptions options = HeapOptions::Default());
virtual ~Heap() = default;
/**
* Forces garbage collection.
*
* \param source String specifying the source (or caller) triggering a
* forced garbage collection.
* \param reason String specifying the reason for the forced garbage
* collection.
* \param stack_state The embedder stack state, see StackState.
*/
void ForceGarbageCollectionSlow(
const char* source, const char* reason,
StackState stack_state = StackState::kMayContainHeapPointers);
/**
* \returns the opaque handle for allocating objects using
* `MakeGarbageCollected()`.
*/
AllocationHandle& GetAllocationHandle();
/**
* \returns the opaque heap handle which may be used to refer to this heap in
* other APIs. Valid as long as the underlying `Heap` is alive.
*/
HeapHandle& GetHeapHandle();
private:
Heap() = default;
friend class internal::Heap;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_H_

View File

@ -0,0 +1,65 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_
#define INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_
#include <cstddef>
#include <cstdint>
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
// Embedders should not rely on this code!
// Internal constants to avoid exposing internal types on the API surface.
namespace api_constants {
constexpr size_t kKB = 1024;
constexpr size_t kMB = kKB * 1024;
constexpr size_t kGB = kMB * 1024;
// Offset of the uint16_t bitfield from the payload contaning the
// in-construction bit. This is subtracted from the payload pointer to get
// to the right bitfield.
static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload =
2 * sizeof(uint16_t);
// Mask for in-construction bit.
static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1};
static constexpr size_t kPageSize = size_t{1} << 17;
#if defined(V8_TARGET_ARCH_ARM64) && defined(V8_OS_MACOS)
constexpr size_t kGuardPageSize = 0;
#else
constexpr size_t kGuardPageSize = 4096;
#endif
static constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2;
#if defined(CPPGC_CAGED_HEAP)
#if defined(CPPGC_2GB_CAGE)
constexpr size_t kCagedHeapReservationSize = static_cast<size_t>(2) * kGB;
#else // !defined(CPPGC_2GB_CAGE)
constexpr size_t kCagedHeapReservationSize = static_cast<size_t>(4) * kGB;
#endif // !defined(CPPGC_2GB_CAGE)
constexpr size_t kCagedHeapReservationAlignment = kCagedHeapReservationSize;
#endif // defined(CPPGC_CAGED_HEAP)
static constexpr size_t kDefaultAlignment = sizeof(void*);
// Maximum support alignment for a type as in `alignof(T)`.
static constexpr size_t kMaxSupportedAlignment = 2 * kDefaultAlignment;
// Granularity of heap allocations.
constexpr size_t kAllocationGranularity = sizeof(void*);
} // namespace api_constants
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_

View File

@ -0,0 +1,48 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_
#define INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_
#include <atomic>
namespace cppgc {
namespace internal {
// A flag which provides a fast check whether a scope may be entered on the
// current thread, without needing to access thread-local storage or mutex. Can
// have false positives (i.e., spuriously report that it might be entered), so
// it is expected that this will be used in tandem with a precise check that the
// scope is in fact entered on that thread.
//
// Example:
// g_frobnicating_flag.MightBeEntered() &&
// ThreadLocalFrobnicator().IsFrobnicating()
//
// Relaxed atomic operations are sufficient, since:
// - all accesses remain atomic
// - each thread must observe its own operations in order
// - no thread ever exits the flag more times than it enters (if used correctly)
// And so if a thread observes zero, it must be because it has observed an equal
// number of exits as entries.
class AtomicEntryFlag final {
public:
void Enter() { entries_.fetch_add(1, std::memory_order_relaxed); }
void Exit() { entries_.fetch_sub(1, std::memory_order_relaxed); }
// Returns false only if the current thread is not between a call to Enter
// and a call to Exit. Returns true if this thread or another thread may
// currently be in the scope guarded by this flag.
bool MightBeEntered() const {
return entries_.load(std::memory_order_relaxed) != 0;
}
private:
std::atomic_int entries_{0};
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_

View File

@ -0,0 +1,45 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_
#define INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_
#include "cppgc/heap-handle.h"
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/logging.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
// The class is needed in the header to allow for fast access to HeapHandle in
// the write barrier.
class BasePageHandle {
public:
static V8_INLINE BasePageHandle* FromPayload(void* payload) {
return reinterpret_cast<BasePageHandle*>(
(reinterpret_cast<uintptr_t>(payload) &
~(api_constants::kPageSize - 1)) +
api_constants::kGuardPageSize);
}
static V8_INLINE const BasePageHandle* FromPayload(const void* payload) {
return FromPayload(const_cast<void*>(payload));
}
HeapHandle& heap_handle() { return heap_handle_; }
const HeapHandle& heap_handle() const { return heap_handle_; }
protected:
explicit BasePageHandle(HeapHandle& heap_handle) : heap_handle_(heap_handle) {
CPPGC_DCHECK(reinterpret_cast<uintptr_t>(this) % api_constants::kPageSize ==
api_constants::kGuardPageSize);
}
HeapHandle& heap_handle_;
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_

View File

@ -0,0 +1,111 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_
#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_
#include <array>
#include <cstddef>
#include <cstdint>
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/caged-heap.h"
#include "cppgc/internal/logging.h"
#include "cppgc/platform.h"
#include "v8config.h" // NOLINT(build/include_directory)
#if __cpp_lib_bitopts
#include <bit>
#endif // __cpp_lib_bitopts
#if defined(CPPGC_CAGED_HEAP)
namespace cppgc {
namespace internal {
class HeapBase;
class HeapBaseHandle;
#if defined(CPPGC_YOUNG_GENERATION)
// AgeTable is the bytemap needed for the fast generation check in the write
// barrier. AgeTable contains entries that correspond to 4096 bytes memory
// regions (cards). Each entry in the table represents generation of the objects
// that reside on the corresponding card (young, old or mixed).
class V8_EXPORT AgeTable final {
static constexpr size_t kRequiredSize = 1 * api_constants::kMB;
static constexpr size_t kAllocationGranularity =
api_constants::kAllocationGranularity;
public:
// Represents age of the objects living on a single card.
enum class Age : uint8_t { kOld, kYoung, kMixed };
// When setting age for a range, consider or ignore ages of the adjacent
// cards.
enum class AdjacentCardsPolicy : uint8_t { kConsider, kIgnore };
static constexpr size_t kCardSizeInBytes =
api_constants::kCagedHeapReservationSize / kRequiredSize;
void SetAge(uintptr_t cage_offset, Age age) {
table_[card(cage_offset)] = age;
}
V8_INLINE Age GetAge(uintptr_t cage_offset) const {
return table_[card(cage_offset)];
}
void SetAgeForRange(uintptr_t cage_offset_begin, uintptr_t cage_offset_end,
Age age, AdjacentCardsPolicy adjacent_cards_policy);
Age GetAgeForRange(uintptr_t cage_offset_begin,
uintptr_t cage_offset_end) const;
void ResetForTesting();
private:
V8_INLINE size_t card(uintptr_t offset) const {
constexpr size_t kGranularityBits =
#if __cpp_lib_bitopts
std::countr_zero(static_cast<uint32_t>(kCardSizeInBytes));
#elif V8_HAS_BUILTIN_CTZ
__builtin_ctz(static_cast<uint32_t>(kCardSizeInBytes));
#else //! V8_HAS_BUILTIN_CTZ
// Hardcode and check with assert.
#if defined(CPPGC_2GB_CAGE)
11;
#else // !defined(CPPGC_2GB_CAGE)
12;
#endif // !defined(CPPGC_2GB_CAGE)
#endif // !V8_HAS_BUILTIN_CTZ
static_assert((1 << kGranularityBits) == kCardSizeInBytes);
const size_t entry = offset >> kGranularityBits;
CPPGC_DCHECK(table_.size() > entry);
return entry;
}
std::array<Age, kRequiredSize> table_;
};
static_assert(sizeof(AgeTable) == 1 * api_constants::kMB,
"Size of AgeTable is 1MB");
#endif // CPPGC_YOUNG_GENERATION
struct CagedHeapLocalData final {
V8_INLINE static CagedHeapLocalData& Get() {
return *reinterpret_cast<CagedHeapLocalData*>(CagedHeapBase::GetBase());
}
#if defined(CPPGC_YOUNG_GENERATION)
AgeTable age_table;
#endif
};
} // namespace internal
} // namespace cppgc
#endif // defined(CPPGC_CAGED_HEAP)
#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_

View File

@ -0,0 +1,61 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_
#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_
#include <climits>
#include <cstddef>
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/base-page-handle.h"
#include "v8config.h" // NOLINT(build/include_directory)
#if defined(CPPGC_CAGED_HEAP)
namespace cppgc {
namespace internal {
class V8_EXPORT CagedHeapBase {
public:
V8_INLINE static uintptr_t OffsetFromAddress(const void* address) {
return reinterpret_cast<uintptr_t>(address) &
(api_constants::kCagedHeapReservationAlignment - 1);
}
V8_INLINE static bool IsWithinCage(const void* address) {
CPPGC_DCHECK(g_heap_base_);
return (reinterpret_cast<uintptr_t>(address) &
~(api_constants::kCagedHeapReservationAlignment - 1)) ==
g_heap_base_;
}
V8_INLINE static bool AreWithinCage(const void* addr1, const void* addr2) {
#if defined(CPPGC_2GB_CAGE)
static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT - 1;
#else //! defined(CPPGC_2GB_CAGE)
static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT;
#endif //! defined(CPPGC_2GB_CAGE)
static_assert((static_cast<size_t>(1) << kHalfWordShift) ==
api_constants::kCagedHeapReservationSize);
CPPGC_DCHECK(g_heap_base_);
return !(((reinterpret_cast<uintptr_t>(addr1) ^ g_heap_base_) |
(reinterpret_cast<uintptr_t>(addr2) ^ g_heap_base_)) >>
kHalfWordShift);
}
V8_INLINE static uintptr_t GetBase() { return g_heap_base_; }
private:
friend class CagedHeap;
static uintptr_t g_heap_base_;
};
} // namespace internal
} // namespace cppgc
#endif // defined(CPPGC_CAGED_HEAP)
#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_

View File

@ -0,0 +1,38 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_
#define INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_
namespace cppgc {
#if defined(__has_attribute)
#define CPPGC_HAS_ATTRIBUTE(FEATURE) __has_attribute(FEATURE)
#else
#define CPPGC_HAS_ATTRIBUTE(FEATURE) 0
#endif
#if defined(__has_cpp_attribute)
#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) __has_cpp_attribute(FEATURE)
#else
#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) 0
#endif
// [[no_unique_address]] comes in C++20 but supported in clang with -std >=
// c++11.
#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address)
#define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define CPPGC_NO_UNIQUE_ADDRESS
#endif
#if CPPGC_HAS_ATTRIBUTE(unused)
#define CPPGC_UNUSED __attribute__((unused))
#else
#define CPPGC_UNUSED
#endif
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_

View File

@ -0,0 +1,93 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_
#define INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_
#include <type_traits>
#include "cppgc/type-traits.h"
namespace cppgc {
namespace internal {
using FinalizationCallback = void (*)(void*);
template <typename T, typename = void>
struct HasFinalizeGarbageCollectedObject : std::false_type {};
template <typename T>
struct HasFinalizeGarbageCollectedObject<
T,
std::void_t<decltype(std::declval<T>().FinalizeGarbageCollectedObject())>>
: std::true_type {};
// The FinalizerTraitImpl specifies how to finalize objects.
template <typename T, bool isFinalized>
struct FinalizerTraitImpl;
template <typename T>
struct FinalizerTraitImpl<T, true> {
private:
// Dispatch to custom FinalizeGarbageCollectedObject().
struct Custom {
static void Call(void* obj) {
static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
}
};
// Dispatch to regular destructor.
struct Destructor {
static void Call(void* obj) { static_cast<T*>(obj)->~T(); }
};
using FinalizeImpl =
std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value, Custom,
Destructor>;
public:
static void Finalize(void* obj) {
static_assert(sizeof(T), "T must be fully defined");
FinalizeImpl::Call(obj);
}
};
template <typename T>
struct FinalizerTraitImpl<T, false> {
static void Finalize(void* obj) {
static_assert(sizeof(T), "T must be fully defined");
}
};
// The FinalizerTrait is used to determine if a type requires finalization and
// what finalization means.
template <typename T>
struct FinalizerTrait {
private:
// Object has a finalizer if it has
// - a custom FinalizeGarbageCollectedObject method, or
// - a destructor.
static constexpr bool kNonTrivialFinalizer =
internal::HasFinalizeGarbageCollectedObject<T>::value ||
!std::is_trivially_destructible<typename std::remove_cv<T>::type>::value;
static void Finalize(void* obj) {
internal::FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
}
public:
static constexpr bool HasFinalizer() { return kNonTrivialFinalizer; }
// The callback used to finalize an object of type T.
static constexpr FinalizationCallback kCallback =
kNonTrivialFinalizer ? Finalize : nullptr;
};
template <typename T>
constexpr FinalizationCallback FinalizerTrait<T>::kCallback;
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_

View File

@ -0,0 +1,155 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_
#define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_
#include <atomic>
#include <cstdint>
#include <type_traits>
#include "cppgc/internal/finalizer-trait.h"
#include "cppgc/internal/name-trait.h"
#include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
using GCInfoIndex = uint16_t;
struct V8_EXPORT EnsureGCInfoIndexTrait final {
// Acquires a new GC info object and returns the index. In addition, also
// updates `registered_index` atomically.
template <typename T>
V8_INLINE static GCInfoIndex EnsureIndex(
std::atomic<GCInfoIndex>& registered_index) {
return EnsureGCInfoIndexTraitDispatch<T>{}(registered_index);
}
private:
template <typename T, bool = std::is_polymorphic<T>::value,
bool = FinalizerTrait<T>::HasFinalizer(),
bool = NameTrait<T>::HasNonHiddenName()>
struct EnsureGCInfoIndexTraitDispatch;
static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback,
FinalizationCallback,
NameCallback);
static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback,
FinalizationCallback);
static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback, NameCallback);
static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback);
static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback,
FinalizationCallback,
NameCallback);
static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback,
FinalizationCallback);
static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback,
NameCallback);
static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&,
TraceCallback);
};
#define DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) \
template <typename T> \
struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \
T, is_polymorphic, has_finalizer, has_non_hidden_name> { \
V8_INLINE GCInfoIndex \
operator()(std::atomic<GCInfoIndex>& registered_index) { \
return function; \
} \
};
// --------------------------------------------------------------------- //
// DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function)
// --------------------------------------------------------------------- //
DISPATCH(true, true, true, //
EnsureGCInfoIndexPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
FinalizerTrait<T>::kCallback, //
NameTrait<T>::GetName)) //
DISPATCH(true, true, false, //
EnsureGCInfoIndexPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
FinalizerTrait<T>::kCallback)) //
DISPATCH(true, false, true, //
EnsureGCInfoIndexPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
NameTrait<T>::GetName)) //
DISPATCH(true, false, false, //
EnsureGCInfoIndexPolymorphic(registered_index, //
TraceTrait<T>::Trace)) //
DISPATCH(false, true, true, //
EnsureGCInfoIndexNonPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
FinalizerTrait<T>::kCallback, //
NameTrait<T>::GetName)) //
DISPATCH(false, true, false, //
EnsureGCInfoIndexNonPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
FinalizerTrait<T>::kCallback)) //
DISPATCH(false, false, true, //
EnsureGCInfoIndexNonPolymorphic(registered_index, //
TraceTrait<T>::Trace, //
NameTrait<T>::GetName)) //
DISPATCH(false, false, false, //
EnsureGCInfoIndexNonPolymorphic(registered_index, //
TraceTrait<T>::Trace)) //
#undef DISPATCH
// Fold types based on finalizer behavior. Note that finalizer characteristics
// align with trace behavior, i.e., destructors are virtual when trace methods
// are and vice versa.
template <typename T, typename ParentMostGarbageCollectedType>
struct GCInfoFolding {
static constexpr bool kHasVirtualDestructorAtBase =
std::has_virtual_destructor<ParentMostGarbageCollectedType>::value;
static constexpr bool kBothTypesAreTriviallyDestructible =
std::is_trivially_destructible<ParentMostGarbageCollectedType>::value &&
std::is_trivially_destructible<T>::value;
static constexpr bool kHasCustomFinalizerDispatchAtBase =
internal::HasFinalizeGarbageCollectedObject<
ParentMostGarbageCollectedType>::value;
#ifdef CPPGC_SUPPORTS_OBJECT_NAMES
static constexpr bool kWantsDetailedObjectNames = true;
#else // !CPPGC_SUPPORTS_OBJECT_NAMES
static constexpr bool kWantsDetailedObjectNames = false;
#endif // !CPPGC_SUPPORTS_OBJECT_NAMES
// Folding would regresses name resolution when deriving names from C++
// class names as it would just folds a name to the base class name.
using ResultType = std::conditional_t<(kHasVirtualDestructorAtBase ||
kBothTypesAreTriviallyDestructible ||
kHasCustomFinalizerDispatchAtBase) &&
!kWantsDetailedObjectNames,
ParentMostGarbageCollectedType, T>;
};
// Trait determines how the garbage collector treats objects wrt. to traversing,
// finalization, and naming.
template <typename T>
struct GCInfoTrait final {
V8_INLINE static GCInfoIndex Index() {
static_assert(sizeof(T), "T must be fully defined");
static std::atomic<GCInfoIndex>
registered_index; // Uses zero initialization.
const GCInfoIndex index = registered_index.load(std::memory_order_acquire);
return index ? index
: EnsureGCInfoIndexTrait::EnsureIndex<T>(registered_index);
}
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_

View File

@ -0,0 +1,50 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_LOGGING_H_
#define INCLUDE_CPPGC_INTERNAL_LOGGING_H_
#include "cppgc/source-location.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
void V8_EXPORT DCheckImpl(const char*,
const SourceLocation& = SourceLocation::Current());
[[noreturn]] void V8_EXPORT
FatalImpl(const char*, const SourceLocation& = SourceLocation::Current());
// Used to ignore -Wunused-variable.
template <typename>
struct EatParams {};
#if defined(DEBUG)
#define CPPGC_DCHECK_MSG(condition, message) \
do { \
if (V8_UNLIKELY(!(condition))) { \
::cppgc::internal::DCheckImpl(message); \
} \
} while (false)
#else // !defined(DEBUG)
#define CPPGC_DCHECK_MSG(condition, message) \
(static_cast<void>(::cppgc::internal::EatParams<decltype( \
static_cast<void>(condition), message)>{}))
#endif // !defined(DEBUG)
#define CPPGC_DCHECK(condition) CPPGC_DCHECK_MSG(condition, #condition)
#define CPPGC_CHECK_MSG(condition, message) \
do { \
if (V8_UNLIKELY(!(condition))) { \
::cppgc::internal::FatalImpl(message); \
} \
} while (false)
#define CPPGC_CHECK(condition) CPPGC_CHECK_MSG(condition, #condition)
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_LOGGING_H_

View File

@ -0,0 +1,236 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_
#define INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_
#include <atomic>
#include <cstddef>
#include <type_traits>
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/logging.h"
#include "cppgc/sentinel-pointer.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
#if defined(CPPGC_POINTER_COMPRESSION)
#if defined(__clang__)
// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_
// doesn't change (e.g. across calls) and thereby avoid redundant loads.
#define CPPGC_CONST __attribute__((const))
#define CPPGC_REQUIRE_CONSTANT_INIT \
__attribute__((require_constant_initialization))
#else // defined(__clang__)
#define CPPGC_CONST
#define CPPGC_REQUIRE_CONSTANT_INIT
#endif // defined(__clang__)
class CageBaseGlobal final {
public:
V8_INLINE CPPGC_CONST static uintptr_t Get() {
CPPGC_DCHECK(IsBaseConsistent());
return g_base_;
}
V8_INLINE CPPGC_CONST static bool IsSet() {
CPPGC_DCHECK(IsBaseConsistent());
return (g_base_ & ~kLowerHalfWordMask) != 0;
}
private:
// We keep the lower halfword as ones to speed up decompression.
static constexpr uintptr_t kLowerHalfWordMask =
(api_constants::kCagedHeapReservationAlignment - 1);
static V8_EXPORT uintptr_t g_base_ CPPGC_REQUIRE_CONSTANT_INIT;
CageBaseGlobal() = delete;
V8_INLINE static bool IsBaseConsistent() {
return kLowerHalfWordMask == (g_base_ & kLowerHalfWordMask);
}
friend class CageBaseGlobalUpdater;
};
#undef CPPGC_REQUIRE_CONSTANT_INIT
#undef CPPGC_CONST
class V8_TRIVIAL_ABI CompressedPointer final {
public:
using IntegralType = uint32_t;
V8_INLINE CompressedPointer() : value_(0u) {}
V8_INLINE explicit CompressedPointer(const void* ptr)
: value_(Compress(ptr)) {}
V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {}
V8_INLINE explicit CompressedPointer(SentinelPointer)
: value_(kCompressedSentinel) {}
V8_INLINE const void* Load() const { return Decompress(value_); }
V8_INLINE const void* LoadAtomic() const {
return Decompress(
reinterpret_cast<const std::atomic<IntegralType>&>(value_).load(
std::memory_order_relaxed));
}
V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); }
V8_INLINE void StoreAtomic(const void* value) {
reinterpret_cast<std::atomic<IntegralType>&>(value_).store(
Compress(value), std::memory_order_relaxed);
}
V8_INLINE void Clear() { value_ = 0u; }
V8_INLINE bool IsCleared() const { return !value_; }
V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; }
V8_INLINE uint32_t GetAsInteger() const { return value_; }
V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) {
return a.value_ == b.value_;
}
V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) {
return a.value_ != b.value_;
}
V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) {
return a.value_ < b.value_;
}
V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) {
return a.value_ <= b.value_;
}
V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) {
return a.value_ > b.value_;
}
V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) {
return a.value_ >= b.value_;
}
static V8_INLINE IntegralType Compress(const void* ptr) {
static_assert(
SentinelPointer::kSentinelValue == 0b10,
"The compression scheme relies on the sentinel encoded as 0b10");
static constexpr size_t kGigaCageMask =
~(api_constants::kCagedHeapReservationAlignment - 1);
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
CPPGC_DCHECK(!ptr || ptr == kSentinelPointer ||
(base & kGigaCageMask) ==
(reinterpret_cast<uintptr_t>(ptr) & kGigaCageMask));
#if defined(CPPGC_2GB_CAGE)
// Truncate the pointer.
auto compressed =
static_cast<IntegralType>(reinterpret_cast<uintptr_t>(ptr));
#else // !defined(CPPGC_2GB_CAGE)
const auto uptr = reinterpret_cast<uintptr_t>(ptr);
// Shift the pointer by one and truncate.
auto compressed = static_cast<IntegralType>(uptr >> 1);
#endif // !defined(CPPGC_2GB_CAGE)
// Normal compressed pointers must have the MSB set.
CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) ||
(compressed & (1 << 31)));
return compressed;
}
static V8_INLINE void* Decompress(IntegralType ptr) {
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
// Treat compressed pointer as signed and cast it to uint64_t, which will
// sign-extend it.
#if defined(CPPGC_2GB_CAGE)
const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr));
#else // !defined(CPPGC_2GB_CAGE)
// Then, shift the result by one. It's important to shift the unsigned
// value, as otherwise it would result in undefined behavior.
const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr)) << 1;
#endif // !defined(CPPGC_2GB_CAGE)
return reinterpret_cast<void*>(mask & base);
}
private:
#if defined(CPPGC_2GB_CAGE)
static constexpr IntegralType kCompressedSentinel =
SentinelPointer::kSentinelValue;
#else // !defined(CPPGC_2GB_CAGE)
static constexpr IntegralType kCompressedSentinel =
SentinelPointer::kSentinelValue >> 1;
#endif // !defined(CPPGC_2GB_CAGE)
// All constructors initialize `value_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
IntegralType value_;
};
#endif // defined(CPPGC_POINTER_COMPRESSION)
class V8_TRIVIAL_ABI RawPointer final {
public:
using IntegralType = uintptr_t;
V8_INLINE RawPointer() : ptr_(nullptr) {}
V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {}
V8_INLINE const void* Load() const { return ptr_; }
V8_INLINE const void* LoadAtomic() const {
return reinterpret_cast<const std::atomic<const void*>&>(ptr_).load(
std::memory_order_relaxed);
}
V8_INLINE void Store(const void* ptr) { ptr_ = ptr; }
V8_INLINE void StoreAtomic(const void* ptr) {
reinterpret_cast<std::atomic<const void*>&>(ptr_).store(
ptr, std::memory_order_relaxed);
}
V8_INLINE void Clear() { ptr_ = nullptr; }
V8_INLINE bool IsCleared() const { return !ptr_; }
V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; }
V8_INLINE uintptr_t GetAsInteger() const {
return reinterpret_cast<uintptr_t>(ptr_);
}
V8_INLINE friend bool operator==(RawPointer a, RawPointer b) {
return a.ptr_ == b.ptr_;
}
V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) {
return a.ptr_ != b.ptr_;
}
V8_INLINE friend bool operator<(RawPointer a, RawPointer b) {
return a.ptr_ < b.ptr_;
}
V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) {
return a.ptr_ <= b.ptr_;
}
V8_INLINE friend bool operator>(RawPointer a, RawPointer b) {
return a.ptr_ > b.ptr_;
}
V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) {
return a.ptr_ >= b.ptr_;
}
private:
// All constructors initialize `ptr_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
const void* ptr_;
};
#if defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = CompressedPointer;
#else // !defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = RawPointer;
#endif // !defined(CPPGC_POINTER_COMPRESSION)
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_

View File

@ -0,0 +1,137 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_
#define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "cppgc/name-provider.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
#if CPPGC_SUPPORTS_OBJECT_NAMES && defined(__clang__)
#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 1
// Provides constexpr c-string storage for a name of fixed |Size| characters.
// Automatically appends terminating 0 byte.
template <size_t Size>
struct NameBuffer {
char name[Size + 1]{};
static constexpr NameBuffer FromCString(const char* str) {
NameBuffer result;
for (size_t i = 0; i < Size; ++i) result.name[i] = str[i];
result.name[Size] = 0;
return result;
}
};
template <typename T>
const char* GetTypename() {
static constexpr char kSelfPrefix[] =
"const char *cppgc::internal::GetTypename() [T =";
static_assert(__builtin_strncmp(__PRETTY_FUNCTION__, kSelfPrefix,
sizeof(kSelfPrefix) - 1) == 0,
"The prefix must match");
static constexpr const char* kTypenameStart =
__PRETTY_FUNCTION__ + sizeof(kSelfPrefix);
static constexpr size_t kTypenameSize =
__builtin_strlen(__PRETTY_FUNCTION__) - sizeof(kSelfPrefix) - 1;
// NameBuffer is an indirection that is needed to make sure that only a
// substring of __PRETTY_FUNCTION__ gets materialized in the binary.
static constexpr auto buffer =
NameBuffer<kTypenameSize>::FromCString(kTypenameStart);
return buffer.name;
}
#else
#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 0
#endif
struct HeapObjectName {
const char* value;
bool name_was_hidden;
};
enum class HeapObjectNameForUnnamedObject : uint8_t {
kUseClassNameIfSupported,
kUseHiddenName,
};
class V8_EXPORT NameTraitBase {
protected:
static HeapObjectName GetNameFromTypeSignature(const char*);
};
// Trait that specifies how the garbage collector retrieves the name for a
// given object.
template <typename T>
class NameTrait final : public NameTraitBase {
public:
static constexpr bool HasNonHiddenName() {
#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
return true;
#elif CPPGC_SUPPORTS_OBJECT_NAMES
return true;
#else // !CPPGC_SUPPORTS_OBJECT_NAMES
return std::is_base_of<NameProvider, T>::value;
#endif // !CPPGC_SUPPORTS_OBJECT_NAMES
}
static HeapObjectName GetName(
const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode) {
return GetNameFor(static_cast<const T*>(obj), name_retrieval_mode);
}
private:
static HeapObjectName GetNameFor(const NameProvider* name_provider,
HeapObjectNameForUnnamedObject) {
// Objects inheriting from `NameProvider` are not considered unnamed as
// users already provided a name for them.
return {name_provider->GetHumanReadableName(), false};
}
static HeapObjectName GetNameFor(
const void*, HeapObjectNameForUnnamedObject name_retrieval_mode) {
if (name_retrieval_mode == HeapObjectNameForUnnamedObject::kUseHiddenName)
return {NameProvider::kHiddenName, true};
#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
return {GetTypename<T>(), false};
#elif CPPGC_SUPPORTS_OBJECT_NAMES
#if defined(V8_CC_GNU)
#define PRETTY_FUNCTION_VALUE __PRETTY_FUNCTION__
#elif defined(V8_CC_MSVC)
#define PRETTY_FUNCTION_VALUE __FUNCSIG__
#else
#define PRETTY_FUNCTION_VALUE nullptr
#endif
static const HeapObjectName leaky_name =
GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE);
return leaky_name;
#undef PRETTY_FUNCTION_VALUE
#else // !CPPGC_SUPPORTS_OBJECT_NAMES
return {NameProvider::kHiddenName, true};
#endif // !CPPGC_SUPPORTS_OBJECT_NAMES
}
};
using NameCallback = HeapObjectName (*)(const void*,
HeapObjectNameForUnnamedObject);
} // namespace internal
} // namespace cppgc
#undef CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
#endif // INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_

View File

@ -0,0 +1,214 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_
#define INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_
#include <array>
#include <memory>
#include <vector>
#include "cppgc/internal/logging.h"
#include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class CrossThreadPersistentRegion;
class FatalOutOfMemoryHandler;
class RootVisitor;
// PersistentNode represents a variant of two states:
// 1) traceable node with a back pointer to the Persistent object;
// 2) freelist entry.
class PersistentNode final {
public:
PersistentNode() = default;
PersistentNode(const PersistentNode&) = delete;
PersistentNode& operator=(const PersistentNode&) = delete;
void InitializeAsUsedNode(void* owner, TraceRootCallback trace) {
CPPGC_DCHECK(trace);
owner_ = owner;
trace_ = trace;
}
void InitializeAsFreeNode(PersistentNode* next) {
next_ = next;
trace_ = nullptr;
}
void UpdateOwner(void* owner) {
CPPGC_DCHECK(IsUsed());
owner_ = owner;
}
PersistentNode* FreeListNext() const {
CPPGC_DCHECK(!IsUsed());
return next_;
}
void Trace(RootVisitor& root_visitor) const {
CPPGC_DCHECK(IsUsed());
trace_(root_visitor, owner_);
}
bool IsUsed() const { return trace_; }
void* owner() const {
CPPGC_DCHECK(IsUsed());
return owner_;
}
private:
// PersistentNode acts as a designated union:
// If trace_ != nullptr, owner_ points to the corresponding Persistent handle.
// Otherwise, next_ points to the next freed PersistentNode.
union {
void* owner_ = nullptr;
PersistentNode* next_;
};
TraceRootCallback trace_ = nullptr;
};
class V8_EXPORT PersistentRegionBase {
using PersistentNodeSlots = std::array<PersistentNode, 256u>;
public:
// Clears Persistent fields to avoid stale pointers after heap teardown.
~PersistentRegionBase();
PersistentRegionBase(const PersistentRegionBase&) = delete;
PersistentRegionBase& operator=(const PersistentRegionBase&) = delete;
void Iterate(RootVisitor&);
size_t NodesInUse() const;
void ClearAllUsedNodes();
protected:
explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler);
PersistentNode* TryAllocateNodeFromFreeList(void* owner,
TraceRootCallback trace) {
PersistentNode* node = nullptr;
if (V8_LIKELY(free_list_head_)) {
node = free_list_head_;
free_list_head_ = free_list_head_->FreeListNext();
CPPGC_DCHECK(!node->IsUsed());
node->InitializeAsUsedNode(owner, trace);
nodes_in_use_++;
}
return node;
}
void FreeNode(PersistentNode* node) {
CPPGC_DCHECK(node);
CPPGC_DCHECK(node->IsUsed());
node->InitializeAsFreeNode(free_list_head_);
free_list_head_ = node;
CPPGC_DCHECK(nodes_in_use_ > 0);
nodes_in_use_--;
}
PersistentNode* RefillFreeListAndAllocateNode(void* owner,
TraceRootCallback trace);
private:
template <typename PersistentBaseClass>
void ClearAllUsedNodes();
void RefillFreeList();
std::vector<std::unique_ptr<PersistentNodeSlots>> nodes_;
PersistentNode* free_list_head_ = nullptr;
size_t nodes_in_use_ = 0;
const FatalOutOfMemoryHandler& oom_handler_;
friend class CrossThreadPersistentRegion;
};
// Variant of PersistentRegionBase that checks whether the allocation and
// freeing happens only on the thread that created the region.
class V8_EXPORT PersistentRegion final : public PersistentRegionBase {
public:
explicit PersistentRegion(const FatalOutOfMemoryHandler&);
// Clears Persistent fields to avoid stale pointers after heap teardown.
~PersistentRegion() = default;
PersistentRegion(const PersistentRegion&) = delete;
PersistentRegion& operator=(const PersistentRegion&) = delete;
V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) {
CPPGC_DCHECK(IsCreationThread());
auto* node = TryAllocateNodeFromFreeList(owner, trace);
if (V8_LIKELY(node)) return node;
// Slow path allocation allows for checking thread correspondence.
CPPGC_CHECK(IsCreationThread());
return RefillFreeListAndAllocateNode(owner, trace);
}
V8_INLINE void FreeNode(PersistentNode* node) {
CPPGC_DCHECK(IsCreationThread());
PersistentRegionBase::FreeNode(node);
}
private:
bool IsCreationThread();
int creation_thread_id_;
};
// CrossThreadPersistent uses PersistentRegionBase but protects it using this
// lock when needed.
class V8_EXPORT PersistentRegionLock final {
public:
PersistentRegionLock();
~PersistentRegionLock();
static void AssertLocked();
};
// Variant of PersistentRegionBase that checks whether the PersistentRegionLock
// is locked.
class V8_EXPORT CrossThreadPersistentRegion final
: protected PersistentRegionBase {
public:
explicit CrossThreadPersistentRegion(const FatalOutOfMemoryHandler&);
// Clears Persistent fields to avoid stale pointers after heap teardown.
~CrossThreadPersistentRegion();
CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete;
CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) =
delete;
V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) {
PersistentRegionLock::AssertLocked();
auto* node = TryAllocateNodeFromFreeList(owner, trace);
if (V8_LIKELY(node)) return node;
return RefillFreeListAndAllocateNode(owner, trace);
}
V8_INLINE void FreeNode(PersistentNode* node) {
PersistentRegionLock::AssertLocked();
PersistentRegionBase::FreeNode(node);
}
void Iterate(RootVisitor&);
size_t NodesInUse() const;
void ClearAllUsedNodes();
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_

View File

@ -0,0 +1,207 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_
#define INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_
#include <cstdint>
#include <type_traits>
#include "cppgc/internal/member-storage.h"
#include "cppgc/internal/write-barrier.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/source-location.h"
#include "cppgc/type-traits.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class HeapBase;
class PersistentRegion;
class CrossThreadPersistentRegion;
// Tags to distinguish between strong and weak member types.
class StrongMemberTag;
class WeakMemberTag;
class UntracedMemberTag;
struct DijkstraWriteBarrierPolicy {
V8_INLINE static void InitializingBarrier(const void*, const void*) {
// Since in initializing writes the source object is always white, having no
// barrier doesn't break the tri-color invariant.
}
V8_INLINE static void AssigningBarrier(const void* slot, const void* value) {
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, value, params);
WriteBarrier(type, params, slot, value);
}
V8_INLINE static void AssigningBarrier(const void* slot,
MemberStorage storage) {
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, storage, params);
WriteBarrier(type, params, slot, storage.Load());
}
private:
V8_INLINE static void WriteBarrier(WriteBarrier::Type type,
const WriteBarrier::Params& params,
const void* slot, const void* value) {
switch (type) {
case WriteBarrier::Type::kGenerational:
WriteBarrier::GenerationalBarrier<
WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot);
break;
case WriteBarrier::Type::kMarking:
WriteBarrier::DijkstraMarkingBarrier(params, value);
break;
case WriteBarrier::Type::kNone:
break;
}
}
};
struct NoWriteBarrierPolicy {
V8_INLINE static void InitializingBarrier(const void*, const void*) {}
V8_INLINE static void AssigningBarrier(const void*, const void*) {}
V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {}
};
class V8_EXPORT SameThreadEnabledCheckingPolicyBase {
protected:
void CheckPointerImpl(const void* ptr, bool points_to_payload,
bool check_off_heap_assignments);
const HeapBase* heap_ = nullptr;
};
template <bool kCheckOffHeapAssignments>
class V8_EXPORT SameThreadEnabledCheckingPolicy
: private SameThreadEnabledCheckingPolicyBase {
protected:
template <typename T>
void CheckPointer(const T* ptr) {
if (!ptr || (kSentinelPointer == ptr)) return;
CheckPointersImplTrampoline<T>::Call(this, ptr);
}
private:
template <typename T, bool = IsCompleteV<T>>
struct CheckPointersImplTrampoline {
static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) {
policy->CheckPointerImpl(ptr, false, kCheckOffHeapAssignments);
}
};
template <typename T>
struct CheckPointersImplTrampoline<T, true> {
static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) {
policy->CheckPointerImpl(ptr, IsGarbageCollectedTypeV<T>,
kCheckOffHeapAssignments);
}
};
};
class DisabledCheckingPolicy {
protected:
V8_INLINE void CheckPointer(const void*) {}
};
#ifdef DEBUG
// Off heap members are not connected to object graph and thus cannot ressurect
// dead objects.
using DefaultMemberCheckingPolicy =
SameThreadEnabledCheckingPolicy<false /* kCheckOffHeapAssignments*/>;
using DefaultPersistentCheckingPolicy =
SameThreadEnabledCheckingPolicy<true /* kCheckOffHeapAssignments*/>;
#else // !DEBUG
using DefaultMemberCheckingPolicy = DisabledCheckingPolicy;
using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy;
#endif // !DEBUG
// For CT(W)P neither marking information (for value), nor objectstart bitmap
// (for slot) are guaranteed to be present because there's no synchronization
// between heaps after marking.
using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy;
class KeepLocationPolicy {
public:
constexpr const SourceLocation& Location() const { return location_; }
protected:
constexpr KeepLocationPolicy() = default;
constexpr explicit KeepLocationPolicy(const SourceLocation& location)
: location_(location) {}
// KeepLocationPolicy must not copy underlying source locations.
KeepLocationPolicy(const KeepLocationPolicy&) = delete;
KeepLocationPolicy& operator=(const KeepLocationPolicy&) = delete;
// Location of the original moved from object should be preserved.
KeepLocationPolicy(KeepLocationPolicy&&) = default;
KeepLocationPolicy& operator=(KeepLocationPolicy&&) = default;
private:
SourceLocation location_;
};
class IgnoreLocationPolicy {
public:
constexpr SourceLocation Location() const { return {}; }
protected:
constexpr IgnoreLocationPolicy() = default;
constexpr explicit IgnoreLocationPolicy(const SourceLocation&) {}
};
#if CPPGC_SUPPORTS_OBJECT_NAMES
using DefaultLocationPolicy = KeepLocationPolicy;
#else
using DefaultLocationPolicy = IgnoreLocationPolicy;
#endif
struct StrongPersistentPolicy {
using IsStrongPersistent = std::true_type;
static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object);
};
struct WeakPersistentPolicy {
using IsStrongPersistent = std::false_type;
static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object);
};
struct StrongCrossThreadPersistentPolicy {
using IsStrongPersistent = std::true_type;
static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
const void* object);
};
struct WeakCrossThreadPersistentPolicy {
using IsStrongPersistent = std::false_type;
static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
const void* object);
};
// Forward declarations setting up the default policies.
template <typename T, typename WeaknessPolicy,
typename LocationPolicy = DefaultLocationPolicy,
typename CheckingPolicy = DefaultCrossThreadPersistentCheckingPolicy>
class BasicCrossThreadPersistent;
template <typename T, typename WeaknessPolicy,
typename LocationPolicy = DefaultLocationPolicy,
typename CheckingPolicy = DefaultPersistentCheckingPolicy>
class BasicPersistent;
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy = DefaultMemberCheckingPolicy>
class BasicMember;
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_

View File

@ -0,0 +1,477 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_
#define INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_
#include <cstddef>
#include <cstdint>
#include "cppgc/heap-handle.h"
#include "cppgc/heap-state.h"
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/atomic-entry-flag.h"
#include "cppgc/internal/base-page-handle.h"
#include "cppgc/internal/member-storage.h"
#include "cppgc/platform.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory)
#if defined(CPPGC_CAGED_HEAP)
#include "cppgc/internal/caged-heap-local-data.h"
#include "cppgc/internal/caged-heap.h"
#endif
namespace cppgc {
class HeapHandle;
namespace internal {
#if defined(CPPGC_CAGED_HEAP)
class WriteBarrierTypeForCagedHeapPolicy;
#else // !CPPGC_CAGED_HEAP
class WriteBarrierTypeForNonCagedHeapPolicy;
#endif // !CPPGC_CAGED_HEAP
class V8_EXPORT WriteBarrier final {
public:
enum class Type : uint8_t {
kNone,
kMarking,
kGenerational,
};
enum class GenerationalBarrierType : uint8_t {
kPreciseSlot,
kPreciseUncompressedSlot,
kImpreciseSlot,
};
struct Params {
HeapHandle* heap = nullptr;
#if V8_ENABLE_CHECKS
Type type = Type::kNone;
#endif // !V8_ENABLE_CHECKS
#if defined(CPPGC_CAGED_HEAP)
uintptr_t slot_offset = 0;
uintptr_t value_offset = 0;
#endif // CPPGC_CAGED_HEAP
};
enum class ValueMode {
kValuePresent,
kNoValuePresent,
};
// Returns the required write barrier for a given `slot` and `value`.
static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value,
Params& params);
// Returns the required write barrier for a given `slot` and `value`.
static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage,
Params& params);
// Returns the required write barrier for a given `slot`.
template <typename HeapHandleCallback>
static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params,
HeapHandleCallback callback);
// Returns the required write barrier for a given `value`.
static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params);
static V8_INLINE void DijkstraMarkingBarrier(const Params& params,
const void* object);
static V8_INLINE void DijkstraMarkingBarrierRange(
const Params& params, const void* first_element, size_t element_size,
size_t number_of_elements, TraceCallback trace_callback);
static V8_INLINE void SteeleMarkingBarrier(const Params& params,
const void* object);
#if defined(CPPGC_YOUNG_GENERATION)
template <GenerationalBarrierType>
static V8_INLINE void GenerationalBarrier(const Params& params,
const void* slot);
#else // !CPPGC_YOUNG_GENERATION
template <GenerationalBarrierType>
static V8_INLINE void GenerationalBarrier(const Params& params,
const void* slot){}
#endif // CPPGC_YOUNG_GENERATION
#if V8_ENABLE_CHECKS
static void CheckParams(Type expected_type, const Params& params);
#else // !V8_ENABLE_CHECKS
static void CheckParams(Type expected_type, const Params& params) {}
#endif // !V8_ENABLE_CHECKS
// The FlagUpdater class allows cppgc internal to update
// |write_barrier_enabled_|.
class FlagUpdater;
static bool IsEnabled() { return write_barrier_enabled_.MightBeEntered(); }
private:
WriteBarrier() = delete;
#if defined(CPPGC_CAGED_HEAP)
using WriteBarrierTypePolicy = WriteBarrierTypeForCagedHeapPolicy;
#else // !CPPGC_CAGED_HEAP
using WriteBarrierTypePolicy = WriteBarrierTypeForNonCagedHeapPolicy;
#endif // !CPPGC_CAGED_HEAP
static void DijkstraMarkingBarrierSlow(const void* value);
static void DijkstraMarkingBarrierSlowWithSentinelCheck(const void* value);
static void DijkstraMarkingBarrierRangeSlow(HeapHandle& heap_handle,
const void* first_element,
size_t element_size,
size_t number_of_elements,
TraceCallback trace_callback);
static void SteeleMarkingBarrierSlow(const void* value);
static void SteeleMarkingBarrierSlowWithSentinelCheck(const void* value);
#if defined(CPPGC_YOUNG_GENERATION)
static CagedHeapLocalData& GetLocalData(HeapHandle&);
static void GenerationalBarrierSlow(const CagedHeapLocalData& local_data,
const AgeTable& age_table,
const void* slot, uintptr_t value_offset,
HeapHandle* heap_handle);
static void GenerationalBarrierForUncompressedSlotSlow(
const CagedHeapLocalData& local_data, const AgeTable& age_table,
const void* slot, uintptr_t value_offset, HeapHandle* heap_handle);
static void GenerationalBarrierForSourceObjectSlow(
const CagedHeapLocalData& local_data, const void* object,
HeapHandle* heap_handle);
#endif // CPPGC_YOUNG_GENERATION
static AtomicEntryFlag write_barrier_enabled_;
};
template <WriteBarrier::Type type>
V8_INLINE WriteBarrier::Type SetAndReturnType(WriteBarrier::Params& params) {
if constexpr (type == WriteBarrier::Type::kNone)
return WriteBarrier::Type::kNone;
#if V8_ENABLE_CHECKS
params.type = type;
#endif // !V8_ENABLE_CHECKS
return type;
}
#if defined(CPPGC_CAGED_HEAP)
class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final {
public:
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
return GetNoSlot(value, params, callback);
}
private:
WriteBarrierTypeForCagedHeapPolicy() = delete;
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value,
WriteBarrier::Params& params,
HeapHandleCallback) {
const bool within_cage = CagedHeapBase::IsWithinCage(value);
if (!within_cage) return WriteBarrier::Type::kNone;
// We know that |value| points either within the normal page or to the
// beginning of large-page, so extract the page header by bitmasking.
BasePageHandle* page =
BasePageHandle::FromPayload(const_cast<void*>(value));
HeapHandle& heap_handle = page->heap_handle();
if (V8_UNLIKELY(heap_handle.is_incremental_marking_in_progress())) {
return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
}
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
template <WriteBarrier::ValueMode value_mode>
struct ValueModeDispatch;
};
template <>
struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
WriteBarrier::ValueMode::kValuePresent> {
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot,
MemberStorage storage,
WriteBarrier::Params& params,
HeapHandleCallback) {
if (V8_LIKELY(!WriteBarrier::IsEnabled()))
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
return BarrierEnabledGet(slot, storage.Load(), params);
}
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
WriteBarrier::Params& params,
HeapHandleCallback) {
if (V8_LIKELY(!WriteBarrier::IsEnabled()))
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
return BarrierEnabledGet(slot, value, params);
}
private:
static V8_INLINE WriteBarrier::Type BarrierEnabledGet(
const void* slot, const void* value, WriteBarrier::Params& params) {
const bool within_cage = CagedHeapBase::AreWithinCage(slot, value);
if (!within_cage) return WriteBarrier::Type::kNone;
// We know that |value| points either within the normal page or to the
// beginning of large-page, so extract the page header by bitmasking.
BasePageHandle* page =
BasePageHandle::FromPayload(const_cast<void*>(value));
HeapHandle& heap_handle = page->heap_handle();
if (V8_LIKELY(!heap_handle.is_incremental_marking_in_progress())) {
#if defined(CPPGC_YOUNG_GENERATION)
if (!heap_handle.is_young_generation_enabled())
return WriteBarrier::Type::kNone;
params.heap = &heap_handle;
params.slot_offset = CagedHeapBase::OffsetFromAddress(slot);
params.value_offset = CagedHeapBase::OffsetFromAddress(value);
return SetAndReturnType<WriteBarrier::Type::kGenerational>(params);
#else // !CPPGC_YOUNG_GENERATION
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
#endif // !CPPGC_YOUNG_GENERATION
}
// Use marking barrier.
params.heap = &heap_handle;
return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
}
};
template <>
struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
WriteBarrier::ValueMode::kNoValuePresent> {
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, const void*,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
if (V8_LIKELY(!WriteBarrier::IsEnabled()))
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
HeapHandle& handle = callback();
#if defined(CPPGC_YOUNG_GENERATION)
if (V8_LIKELY(!handle.is_incremental_marking_in_progress())) {
if (!handle.is_young_generation_enabled()) {
return WriteBarrier::Type::kNone;
}
params.heap = &handle;
// Check if slot is on stack.
if (V8_UNLIKELY(!CagedHeapBase::IsWithinCage(slot))) {
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
params.slot_offset = CagedHeapBase::OffsetFromAddress(slot);
return SetAndReturnType<WriteBarrier::Type::kGenerational>(params);
}
#else // !defined(CPPGC_YOUNG_GENERATION)
if (V8_UNLIKELY(!handle.is_incremental_marking_in_progress())) {
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
#endif // !defined(CPPGC_YOUNG_GENERATION)
params.heap = &handle;
return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
}
};
#endif // CPPGC_CAGED_HEAP
class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final {
public:
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
// `MemberStorage` will always be `RawPointer` for non-caged heap builds.
// Just convert to `void*` in this case.
return ValueModeDispatch<value_mode>::Get(slot, value.Load(), params,
callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
// The slot will never be used in `Get()` below.
return Get<WriteBarrier::ValueMode::kValuePresent>(nullptr, value, params,
callback);
}
private:
template <WriteBarrier::ValueMode value_mode>
struct ValueModeDispatch;
WriteBarrierTypeForNonCagedHeapPolicy() = delete;
};
template <>
struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch<
WriteBarrier::ValueMode::kValuePresent> {
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void*, const void* object,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
// The following check covers nullptr as well as sentinel pointer.
if (object <= static_cast<void*>(kSentinelPointer)) {
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
if (V8_LIKELY(!WriteBarrier::IsEnabled())) {
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
// We know that |object| is within the normal page or in the beginning of a
// large page, so extract the page header by bitmasking.
BasePageHandle* page =
BasePageHandle::FromPayload(const_cast<void*>(object));
HeapHandle& heap_handle = page->heap_handle();
if (V8_LIKELY(heap_handle.is_incremental_marking_in_progress())) {
return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
}
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
}
};
template <>
struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch<
WriteBarrier::ValueMode::kNoValuePresent> {
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void*, const void*,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
if (V8_UNLIKELY(WriteBarrier::IsEnabled())) {
HeapHandle& handle = callback();
if (V8_LIKELY(handle.is_incremental_marking_in_progress())) {
params.heap = &handle;
return SetAndReturnType<WriteBarrier::Type::kMarking>(params);
}
}
return WriteBarrier::Type::kNone;
}
};
// static
WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
const void* slot, const void* value, WriteBarrier::Params& params) {
return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(slot, value,
params, []() {});
}
// static
WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
const void* slot, MemberStorage value, WriteBarrier::Params& params) {
return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(slot, value,
params, []() {});
}
// static
template <typename HeapHandleCallback>
WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
const void* slot, WriteBarrier::Params& params,
HeapHandleCallback callback) {
return WriteBarrierTypePolicy::Get<ValueMode::kNoValuePresent>(
slot, nullptr, params, callback);
}
// static
WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
const void* value, WriteBarrier::Params& params) {
return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(value, params,
[]() {});
}
// static
void WriteBarrier::DijkstraMarkingBarrier(const Params& params,
const void* object) {
CheckParams(Type::kMarking, params);
#if defined(CPPGC_CAGED_HEAP)
// Caged heap already filters out sentinels.
DijkstraMarkingBarrierSlow(object);
#else // !CPPGC_CAGED_HEAP
DijkstraMarkingBarrierSlowWithSentinelCheck(object);
#endif // !CPPGC_CAGED_HEAP
}
// static
void WriteBarrier::DijkstraMarkingBarrierRange(const Params& params,
const void* first_element,
size_t element_size,
size_t number_of_elements,
TraceCallback trace_callback) {
CheckParams(Type::kMarking, params);
DijkstraMarkingBarrierRangeSlow(*params.heap, first_element, element_size,
number_of_elements, trace_callback);
}
// static
void WriteBarrier::SteeleMarkingBarrier(const Params& params,
const void* object) {
CheckParams(Type::kMarking, params);
#if defined(CPPGC_CAGED_HEAP)
// Caged heap already filters out sentinels.
SteeleMarkingBarrierSlow(object);
#else // !CPPGC_CAGED_HEAP
SteeleMarkingBarrierSlowWithSentinelCheck(object);
#endif // !CPPGC_CAGED_HEAP
}
#if defined(CPPGC_YOUNG_GENERATION)
// static
template <WriteBarrier::GenerationalBarrierType type>
void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) {
CheckParams(Type::kGenerational, params);
const CagedHeapLocalData& local_data = CagedHeapLocalData::Get();
const AgeTable& age_table = local_data.age_table;
// Bail out if the slot (precise or imprecise) is in young generation.
if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung))
return;
// Dispatch between different types of barriers.
// TODO(chromium:1029379): Consider reload local_data in the slow path to
// reduce register pressure.
if constexpr (type == GenerationalBarrierType::kPreciseSlot) {
GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset,
params.heap);
} else if constexpr (type ==
GenerationalBarrierType::kPreciseUncompressedSlot) {
GenerationalBarrierForUncompressedSlotSlow(
local_data, age_table, slot, params.value_offset, params.heap);
} else {
GenerationalBarrierForSourceObjectSlow(local_data, slot, params.heap);
}
}
#endif // !CPPGC_YOUNG_GENERATION
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_

View File

@ -0,0 +1,78 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_LIVENESS_BROKER_H_
#define INCLUDE_CPPGC_LIVENESS_BROKER_H_
#include "cppgc/heap.h"
#include "cppgc/member.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class LivenessBrokerFactory;
} // namespace internal
/**
* The broker is passed to weak callbacks to allow (temporarily) querying
* the liveness state of an object. References to non-live objects must be
* cleared when `IsHeapObjectAlive()` returns false.
*
* \code
* class GCedWithCustomWeakCallback final
* : public GarbageCollected<GCedWithCustomWeakCallback> {
* public:
* UntracedMember<Bar> bar;
*
* void CustomWeakCallbackMethod(const LivenessBroker& broker) {
* if (!broker.IsHeapObjectAlive(bar))
* bar = nullptr;
* }
*
* void Trace(cppgc::Visitor* visitor) const {
* visitor->RegisterWeakCallbackMethod<
* GCedWithCustomWeakCallback,
* &GCedWithCustomWeakCallback::CustomWeakCallbackMethod>(this);
* }
* };
* \endcode
*/
class V8_EXPORT LivenessBroker final {
public:
template <typename T>
bool IsHeapObjectAlive(const T* object) const {
// - nullptr objects are considered alive to allow weakness to be used from
// stack while running into a conservative GC. Treating nullptr as dead
// would mean that e.g. custom collections could not be strongified on
// stack.
// - Sentinel pointers are also preserved in weakness and not cleared.
return !object || object == kSentinelPointer ||
IsHeapObjectAliveImpl(
TraceTrait<T>::GetTraceDescriptor(object).base_object_payload);
}
template <typename T>
bool IsHeapObjectAlive(const WeakMember<T>& weak_member) const {
return IsHeapObjectAlive<T>(weak_member.Get());
}
template <typename T>
bool IsHeapObjectAlive(const UntracedMember<T>& untraced_member) const {
return IsHeapObjectAlive<T>(untraced_member.Get());
}
private:
LivenessBroker() = default;
bool IsHeapObjectAliveImpl(const void*) const;
friend class internal::LivenessBrokerFactory;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_LIVENESS_BROKER_H_

26
v8/include/cppgc/macros.h Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_MACROS_H_
#define INCLUDE_CPPGC_MACROS_H_
#include <cstddef>
#include "cppgc/internal/compiler-specific.h"
namespace cppgc {
// Use if the object is only stack allocated.
#define CPPGC_STACK_ALLOCATED() \
public: \
using IsStackAllocatedTypeMarker CPPGC_UNUSED = int; \
\
private: \
void* operator new(size_t) = delete; \
void* operator new(size_t, void*) = delete; \
static_assert(true, "Force semicolon.")
} // namespace cppgc
#endif // INCLUDE_CPPGC_MACROS_H_

566
v8/include/cppgc/member.h Normal file
View File

@ -0,0 +1,566 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_MEMBER_H_
#define INCLUDE_CPPGC_MEMBER_H_
#include <atomic>
#include <cstddef>
#include <type_traits>
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/member-storage.h"
#include "cppgc/internal/pointer-policies.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/type-traits.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace subtle {
class HeapConsistency;
} // namespace subtle
class Visitor;
namespace internal {
// MemberBase always refers to the object as const object and defers to
// BasicMember on casting to the right type as needed.
class V8_TRIVIAL_ABI MemberBase {
public:
#if defined(CPPGC_POINTER_COMPRESSION)
using RawStorage = CompressedPointer;
#else // !defined(CPPGC_POINTER_COMPRESSION)
using RawStorage = RawPointer;
#endif // !defined(CPPGC_POINTER_COMPRESSION)
protected:
struct AtomicInitializerTag {};
V8_INLINE MemberBase() = default;
V8_INLINE explicit MemberBase(const void* value) : raw_(value) {}
V8_INLINE MemberBase(const void* value, AtomicInitializerTag) {
SetRawAtomic(value);
}
V8_INLINE explicit MemberBase(RawStorage raw) : raw_(raw) {}
V8_INLINE explicit MemberBase(std::nullptr_t) : raw_(nullptr) {}
V8_INLINE explicit MemberBase(SentinelPointer s) : raw_(s) {}
V8_INLINE const void** GetRawSlot() const {
return reinterpret_cast<const void**>(const_cast<MemberBase*>(this));
}
V8_INLINE const void* GetRaw() const { return raw_.Load(); }
V8_INLINE void SetRaw(void* value) { raw_.Store(value); }
V8_INLINE const void* GetRawAtomic() const { return raw_.LoadAtomic(); }
V8_INLINE void SetRawAtomic(const void* value) { raw_.StoreAtomic(value); }
V8_INLINE RawStorage GetRawStorage() const { return raw_; }
V8_INLINE void SetRawStorageAtomic(RawStorage other) {
reinterpret_cast<std::atomic<RawStorage>&>(raw_).store(
other, std::memory_order_relaxed);
}
V8_INLINE bool IsCleared() const { return raw_.IsCleared(); }
V8_INLINE void ClearFromGC() const { raw_.Clear(); }
private:
friend class MemberDebugHelper;
mutable RawStorage raw_;
};
// The basic class from which all Member classes are 'generated'.
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
class V8_TRIVIAL_ABI BasicMember final : private MemberBase,
private CheckingPolicy {
public:
using PointeeType = T;
V8_INLINE constexpr BasicMember() = default;
V8_INLINE constexpr BasicMember(std::nullptr_t) {} // NOLINT
V8_INLINE BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT
V8_INLINE BasicMember(T* raw) : MemberBase(raw) { // NOLINT
InitializingWriteBarrier(raw);
this->CheckPointer(Get());
}
V8_INLINE BasicMember(T& raw) // NOLINT
: BasicMember(&raw) {}
// Atomic ctor. Using the AtomicInitializerTag forces BasicMember to
// initialize using atomic assignments. This is required for preventing
// data races with concurrent marking.
using AtomicInitializerTag = MemberBase::AtomicInitializerTag;
V8_INLINE BasicMember(std::nullptr_t, AtomicInitializerTag atomic)
: MemberBase(nullptr, atomic) {}
V8_INLINE BasicMember(SentinelPointer s, AtomicInitializerTag atomic)
: MemberBase(s, atomic) {}
V8_INLINE BasicMember(T* raw, AtomicInitializerTag atomic)
: MemberBase(raw, atomic) {
InitializingWriteBarrier(raw);
this->CheckPointer(Get());
}
V8_INLINE BasicMember(T& raw, AtomicInitializerTag atomic)
: BasicMember(&raw, atomic) {}
// Copy ctor.
V8_INLINE BasicMember(const BasicMember& other)
: BasicMember(other.GetRawStorage()) {}
// Heterogeneous copy constructors. When the source pointer have a different
// type, perform a compress-decompress round, because the source pointer may
// need to be adjusted.
template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
typename OtherCheckingPolicy,
std::enable_if_t<internal::IsDecayedSameV<T, U>>* = nullptr>
V8_INLINE BasicMember( // NOLINT
const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other)
: BasicMember(other.GetRawStorage()) {}
template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
typename OtherCheckingPolicy,
std::enable_if_t<internal::IsStrictlyBaseOfV<T, U>>* = nullptr>
V8_INLINE BasicMember( // NOLINT
const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other)
: BasicMember(other.Get()) {}
// Move ctor.
V8_INLINE BasicMember(BasicMember&& other) noexcept
: BasicMember(other.GetRawStorage()) {
other.Clear();
}
// Heterogeneous move constructors. When the source pointer have a different
// type, perform a compress-decompress round, because the source pointer may
// need to be adjusted.
template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
typename OtherCheckingPolicy,
std::enable_if_t<internal::IsDecayedSameV<T, U>>* = nullptr>
V8_INLINE BasicMember(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>&& other) noexcept
: BasicMember(other.GetRawStorage()) {
other.Clear();
}
template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
typename OtherCheckingPolicy,
std::enable_if_t<internal::IsStrictlyBaseOfV<T, U>>* = nullptr>
V8_INLINE BasicMember(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>&& other) noexcept
: BasicMember(other.Get()) {
other.Clear();
}
// Construction from Persistent.
template <typename U, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy,
typename PersistentCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
V8_INLINE BasicMember(const BasicPersistent<U, PersistentWeaknessPolicy,
PersistentLocationPolicy,
PersistentCheckingPolicy>& p)
: BasicMember(p.Get()) {}
// Copy assignment.
V8_INLINE BasicMember& operator=(const BasicMember& other) {
return operator=(other.GetRawStorage());
}
// Heterogeneous copy assignment. When the source pointer have a different
// type, perform a compress-decompress round, because the source pointer may
// need to be adjusted.
template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
typename OtherCheckingPolicy>
V8_INLINE BasicMember& operator=(
const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other) {
if constexpr (internal::IsDecayedSameV<T, U>) {
return operator=(other.GetRawStorage());
} else {
static_assert(internal::IsStrictlyBaseOfV<T, U>);
return operator=(other.Get());
}
}
// Move assignment.
V8_INLINE BasicMember& operator=(BasicMember&& other) noexcept {
operator=(other.GetRawStorage());
other.Clear();
return *this;
}
// Heterogeneous move assignment. When the source pointer have a different
// type, perform a compress-decompress round, because the source pointer may
// need to be adjusted.
template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
typename OtherCheckingPolicy>
V8_INLINE BasicMember& operator=(
BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>&& other) noexcept {
if constexpr (internal::IsDecayedSameV<T, U>) {
operator=(other.GetRawStorage());
} else {
static_assert(internal::IsStrictlyBaseOfV<T, U>);
operator=(other.Get());
}
other.Clear();
return *this;
}
// Assignment from Persistent.
template <typename U, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy,
typename PersistentCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
V8_INLINE BasicMember& operator=(
const BasicPersistent<U, PersistentWeaknessPolicy,
PersistentLocationPolicy, PersistentCheckingPolicy>&
other) {
return operator=(other.Get());
}
V8_INLINE BasicMember& operator=(T* other) {
SetRawAtomic(other);
AssigningWriteBarrier(other);
this->CheckPointer(Get());
return *this;
}
V8_INLINE BasicMember& operator=(std::nullptr_t) {
Clear();
return *this;
}
V8_INLINE BasicMember& operator=(SentinelPointer s) {
SetRawAtomic(s);
return *this;
}
template <typename OtherWeaknessTag, typename OtherBarrierPolicy,
typename OtherCheckingPolicy>
V8_INLINE void Swap(BasicMember<T, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other) {
auto tmp = GetRawStorage();
*this = other;
other = tmp;
}
V8_INLINE explicit operator bool() const { return !IsCleared(); }
V8_INLINE operator T*() const { return Get(); }
V8_INLINE T* operator->() const { return Get(); }
V8_INLINE T& operator*() const { return *Get(); }
// CFI cast exemption to allow passing SentinelPointer through T* and support
// heterogeneous assignments between different Member and Persistent handles
// based on their actual types.
V8_INLINE V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
// Executed by the mutator, hence non atomic load.
//
// The const_cast below removes the constness from MemberBase storage. The
// following static_cast re-adds any constness if specified through the
// user-visible template parameter T.
return static_cast<T*>(const_cast<void*>(MemberBase::GetRaw()));
}
V8_INLINE void Clear() { SetRawStorageAtomic(RawStorage{}); }
V8_INLINE T* Release() {
T* result = Get();
Clear();
return result;
}
V8_INLINE const T** GetSlotForTesting() const {
return reinterpret_cast<const T**>(GetRawSlot());
}
V8_INLINE RawStorage GetRawStorage() const {
return MemberBase::GetRawStorage();
}
private:
V8_INLINE explicit BasicMember(RawStorage raw) : MemberBase(raw) {
InitializingWriteBarrier(Get());
this->CheckPointer(Get());
}
V8_INLINE BasicMember& operator=(RawStorage other) {
SetRawStorageAtomic(other);
AssigningWriteBarrier();
this->CheckPointer(Get());
return *this;
}
V8_INLINE const T* GetRawAtomic() const {
return static_cast<const T*>(MemberBase::GetRawAtomic());
}
V8_INLINE void InitializingWriteBarrier(T* value) const {
WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), value);
}
V8_INLINE void AssigningWriteBarrier(T* value) const {
WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), value);
}
V8_INLINE void AssigningWriteBarrier() const {
WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRawStorage());
}
V8_INLINE void ClearFromGC() const { MemberBase::ClearFromGC(); }
V8_INLINE T* GetFromGC() const { return Get(); }
friend class cppgc::subtle::HeapConsistency;
friend class cppgc::Visitor;
template <typename U>
friend struct cppgc::TraceTrait;
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1>
friend class BasicMember;
};
// Member equality operators.
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator==(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
if constexpr (internal::IsDecayedSameV<T1, T2>) {
// Check compressed pointers if types are the same.
return member1.GetRawStorage() == member2.GetRawStorage();
} else {
static_assert(internal::IsStrictlyBaseOfV<T1, T2> ||
internal::IsStrictlyBaseOfV<T2, T1>);
// Otherwise, check decompressed pointers.
return member1.Get() == member2.Get();
}
}
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator!=(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
return !(member1 == member2);
}
// Equality with raw pointers.
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy, typename U>
V8_INLINE bool operator==(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
U* raw) {
// Never allow comparison with erased pointers.
static_assert(!internal::IsDecayedSameV<void, U>);
if constexpr (internal::IsDecayedSameV<T, U>) {
// Check compressed pointers if types are the same.
return member.GetRawStorage() == MemberBase::RawStorage(raw);
} else if constexpr (internal::IsStrictlyBaseOfV<T, U>) {
// Cast the raw pointer to T, which may adjust the pointer.
return member.GetRawStorage() ==
MemberBase::RawStorage(static_cast<T*>(raw));
} else {
// Otherwise, decompressed the member.
return member.Get() == raw;
}
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy, typename U>
V8_INLINE bool operator!=(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
U* raw) {
return !(member == raw);
}
template <typename T, typename U, typename WeaknessTag,
typename WriteBarrierPolicy, typename CheckingPolicy>
V8_INLINE bool operator==(T* raw,
const BasicMember<U, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return member == raw;
}
template <typename T, typename U, typename WeaknessTag,
typename WriteBarrierPolicy, typename CheckingPolicy>
V8_INLINE bool operator!=(T* raw,
const BasicMember<U, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return !(raw == member);
}
// Equality with sentinel.
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator==(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
SentinelPointer) {
return member.GetRawStorage().IsSentinel();
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator!=(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
SentinelPointer s) {
return !(member == s);
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator==(SentinelPointer s,
const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return member == s;
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator!=(SentinelPointer s,
const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return !(s == member);
}
// Equality with nullptr.
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator==(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
std::nullptr_t) {
return !static_cast<bool>(member);
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator!=(const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member,
std::nullptr_t n) {
return !(member == n);
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator==(std::nullptr_t n,
const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return member == n;
}
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
V8_INLINE bool operator!=(std::nullptr_t n,
const BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& member) {
return !(n == member);
}
// Relational operators.
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator<(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
static_assert(
internal::IsDecayedSameV<T1, T2>,
"Comparison works only for same pointer type modulo cv-qualifiers");
return member1.GetRawStorage() < member2.GetRawStorage();
}
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator<=(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
static_assert(
internal::IsDecayedSameV<T1, T2>,
"Comparison works only for same pointer type modulo cv-qualifiers");
return member1.GetRawStorage() <= member2.GetRawStorage();
}
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator>(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
static_assert(
internal::IsDecayedSameV<T1, T2>,
"Comparison works only for same pointer type modulo cv-qualifiers");
return member1.GetRawStorage() > member2.GetRawStorage();
}
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
V8_INLINE bool operator>=(
const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>&
member1,
const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>&
member2) {
static_assert(
internal::IsDecayedSameV<T1, T2>,
"Comparison works only for same pointer type modulo cv-qualifiers");
return member1.GetRawStorage() >= member2.GetRawStorage();
}
template <typename T, typename WriteBarrierPolicy, typename CheckingPolicy>
struct IsWeak<
internal::BasicMember<T, WeakMemberTag, WriteBarrierPolicy, CheckingPolicy>>
: std::true_type {};
} // namespace internal
/**
* Members are used in classes to contain strong pointers to other garbage
* collected objects. All Member fields of a class must be traced in the class'
* trace method.
*/
template <typename T>
using Member = internal::BasicMember<T, internal::StrongMemberTag,
internal::DijkstraWriteBarrierPolicy>;
/**
* WeakMember is similar to Member in that it is used to point to other garbage
* collected objects. However instead of creating a strong pointer to the
* object, the WeakMember creates a weak pointer, which does not keep the
* pointee alive. Hence if all pointers to to a heap allocated object are weak
* the object will be garbage collected. At the time of GC the weak pointers
* will automatically be set to null.
*/
template <typename T>
using WeakMember = internal::BasicMember<T, internal::WeakMemberTag,
internal::DijkstraWriteBarrierPolicy>;
/**
* UntracedMember is a pointer to an on-heap object that is not traced for some
* reason. Do not use this unless you know what you are doing. Keeping raw
* pointers to on-heap objects is prohibited unless used from stack. Pointee
* must be kept alive through other means.
*/
template <typename T>
using UntracedMember = internal::BasicMember<T, internal::UntracedMemberTag,
internal::NoWriteBarrierPolicy>;
} // namespace cppgc
#endif // INCLUDE_CPPGC_MEMBER_H_

View File

@ -0,0 +1,65 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_NAME_PROVIDER_H_
#define INCLUDE_CPPGC_NAME_PROVIDER_H_
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
/**
* NameProvider allows for providing a human-readable name for garbage-collected
* objects.
*
* There's two cases of names to distinguish:
* a. Explicitly specified names via using NameProvider. Such names are always
* preserved in the system.
* b. Internal names that Oilpan infers from a C++ type on the class hierarchy
* of the object. This is not necessarily the type of the actually
* instantiated object.
*
* Depending on the build configuration, Oilpan may hide names, i.e., represent
* them with kHiddenName, of case b. to avoid exposing internal details.
*/
class V8_EXPORT NameProvider {
public:
/**
* Name that is used when hiding internals.
*/
static constexpr const char kHiddenName[] = "InternalNode";
/**
* Name that is used in case compiler support is missing for composing a name
* from C++ types.
*/
static constexpr const char kNoNameDeducible[] = "<No name>";
/**
* Indicating whether the build supports extracting C++ names as object names.
*
* @returns true if C++ names should be hidden and represented by kHiddenName.
*/
static constexpr bool SupportsCppClassNamesAsObjectNames() {
#if CPPGC_SUPPORTS_OBJECT_NAMES
return true;
#else // !CPPGC_SUPPORTS_OBJECT_NAMES
return false;
#endif // !CPPGC_SUPPORTS_OBJECT_NAMES
}
virtual ~NameProvider() = default;
/**
* Specifies a name for the garbage-collected object. Such names will never
* be hidden, as they are explicitly specified by the user of this API.
*
* @returns a human readable name for the object.
*/
virtual const char* GetHumanReadableName() const = 0;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_NAME_PROVIDER_H_

View File

@ -0,0 +1,58 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_
#define INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_
#include <cstddef>
#include "cppgc/type-traits.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
struct V8_EXPORT BaseObjectSizeTrait {
protected:
static size_t GetObjectSizeForGarbageCollected(const void*);
static size_t GetObjectSizeForGarbageCollectedMixin(const void*);
};
} // namespace internal
namespace subtle {
/**
* Trait specifying how to get the size of an object that was allocated using
* `MakeGarbageCollected()`. Also supports querying the size with an inner
* pointer to a mixin.
*/
template <typename T, bool = IsGarbageCollectedMixinTypeV<T>>
struct ObjectSizeTrait;
template <typename T>
struct ObjectSizeTrait<T, false> : cppgc::internal::BaseObjectSizeTrait {
static_assert(sizeof(T), "T must be fully defined");
static_assert(IsGarbageCollectedTypeV<T>,
"T must be of type GarbageCollected or GarbageCollectedMixin");
static size_t GetSize(const T& object) {
return GetObjectSizeForGarbageCollected(&object);
}
};
template <typename T>
struct ObjectSizeTrait<T, true> : cppgc::internal::BaseObjectSizeTrait {
static_assert(sizeof(T), "T must be fully defined");
static size_t GetSize(const T& object) {
return GetObjectSizeForGarbageCollectedMixin(&object);
}
};
} // namespace subtle
} // namespace cppgc
#endif // INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_

View File

@ -0,0 +1,366 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_PERSISTENT_H_
#define INCLUDE_CPPGC_PERSISTENT_H_
#include <type_traits>
#include "cppgc/internal/persistent-node.h"
#include "cppgc/internal/pointer-policies.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/source-location.h"
#include "cppgc/type-traits.h"
#include "cppgc/visitor.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
// PersistentBase always refers to the object as const object and defers to
// BasicPersistent on casting to the right type as needed.
class PersistentBase {
protected:
PersistentBase() = default;
explicit PersistentBase(const void* raw) : raw_(raw) {}
const void* GetValue() const { return raw_; }
void SetValue(const void* value) { raw_ = value; }
PersistentNode* GetNode() const { return node_; }
void SetNode(PersistentNode* node) { node_ = node; }
// Performs a shallow clear which assumes that internal persistent nodes are
// destroyed elsewhere.
void ClearFromGC() const {
raw_ = nullptr;
node_ = nullptr;
}
protected:
mutable const void* raw_ = nullptr;
mutable PersistentNode* node_ = nullptr;
friend class PersistentRegionBase;
};
// The basic class from which all Persistent classes are generated.
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
class BasicPersistent final : public PersistentBase,
public LocationPolicy,
private WeaknessPolicy,
private CheckingPolicy {
public:
using typename WeaknessPolicy::IsStrongPersistent;
using PointeeType = T;
// Null-state/sentinel constructors.
BasicPersistent( // NOLINT
const SourceLocation& loc = SourceLocation::Current())
: LocationPolicy(loc) {}
BasicPersistent(std::nullptr_t, // NOLINT
const SourceLocation& loc = SourceLocation::Current())
: LocationPolicy(loc) {}
BasicPersistent( // NOLINT
SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
: PersistentBase(s), LocationPolicy(loc) {}
// Raw value constructors.
BasicPersistent(T* raw, // NOLINT
const SourceLocation& loc = SourceLocation::Current())
: PersistentBase(raw), LocationPolicy(loc) {
if (!IsValid()) return;
SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
.AllocateNode(this, &TraceAsRoot));
this->CheckPointer(Get());
}
BasicPersistent(T& raw, // NOLINT
const SourceLocation& loc = SourceLocation::Current())
: BasicPersistent(&raw, loc) {}
// Copy ctor.
BasicPersistent(const BasicPersistent& other,
const SourceLocation& loc = SourceLocation::Current())
: BasicPersistent(other.Get(), loc) {}
// Heterogeneous ctor.
template <typename U, typename OtherWeaknessPolicy,
typename OtherLocationPolicy, typename OtherCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent(
const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>& other,
const SourceLocation& loc = SourceLocation::Current())
: BasicPersistent(other.Get(), loc) {}
// Move ctor. The heterogeneous move ctor is not supported since e.g.
// persistent can't reuse persistent node from weak persistent.
BasicPersistent(
BasicPersistent&& other,
const SourceLocation& loc = SourceLocation::Current()) noexcept
: PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
if (!IsValid()) return;
GetNode()->UpdateOwner(this);
other.SetValue(nullptr);
other.SetNode(nullptr);
this->CheckPointer(Get());
}
// Constructor from member.
template <typename U, typename MemberBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent(
const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
MemberCheckingPolicy>& member,
const SourceLocation& loc = SourceLocation::Current())
: BasicPersistent(member.Get(), loc) {}
~BasicPersistent() { Clear(); }
// Copy assignment.
BasicPersistent& operator=(const BasicPersistent& other) {
return operator=(other.Get());
}
template <typename U, typename OtherWeaknessPolicy,
typename OtherLocationPolicy, typename OtherCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent& operator=(
const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>& other) {
return operator=(other.Get());
}
// Move assignment.
BasicPersistent& operator=(BasicPersistent&& other) noexcept {
if (this == &other) return *this;
Clear();
PersistentBase::operator=(std::move(other));
LocationPolicy::operator=(std::move(other));
if (!IsValid()) return *this;
GetNode()->UpdateOwner(this);
other.SetValue(nullptr);
other.SetNode(nullptr);
this->CheckPointer(Get());
return *this;
}
// Assignment from member.
template <typename U, typename MemberBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent& operator=(
const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
MemberCheckingPolicy>& member) {
return operator=(member.Get());
}
BasicPersistent& operator=(T* other) {
Assign(other);
return *this;
}
BasicPersistent& operator=(std::nullptr_t) {
Clear();
return *this;
}
BasicPersistent& operator=(SentinelPointer s) {
Assign(s);
return *this;
}
explicit operator bool() const { return Get(); }
operator T*() const { return Get(); }
T* operator->() const { return Get(); }
T& operator*() const { return *Get(); }
// CFI cast exemption to allow passing SentinelPointer through T* and support
// heterogeneous assignments between different Member and Persistent handles
// based on their actual types.
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
// The const_cast below removes the constness from PersistentBase storage.
// The following static_cast re-adds any constness if specified through the
// user-visible template parameter T.
return static_cast<T*>(const_cast<void*>(GetValue()));
}
void Clear() {
// Simplified version of `Assign()` to allow calling without a complete type
// `T`.
if (IsValid()) {
WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
SetNode(nullptr);
}
SetValue(nullptr);
}
T* Release() {
T* result = Get();
Clear();
return result;
}
template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
typename OtherLocationPolicy = LocationPolicy,
typename OtherCheckingPolicy = CheckingPolicy>
BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>
To() const {
return BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
OtherCheckingPolicy>(static_cast<U*>(Get()));
}
private:
static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
root_visitor.Trace(*static_cast<const BasicPersistent*>(ptr));
}
bool IsValid() const {
// Ideally, handling kSentinelPointer would be done by the embedder. On the
// other hand, having Persistent aware of it is beneficial since no node
// gets wasted.
return GetValue() != nullptr && GetValue() != kSentinelPointer;
}
void Assign(T* ptr) {
if (IsValid()) {
if (ptr && ptr != kSentinelPointer) {
// Simply assign the pointer reusing the existing node.
SetValue(ptr);
this->CheckPointer(ptr);
return;
}
WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
SetNode(nullptr);
}
SetValue(ptr);
if (!IsValid()) return;
SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
.AllocateNode(this, &TraceAsRoot));
this->CheckPointer(Get());
}
void ClearFromGC() const {
if (IsValid()) {
WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
PersistentBase::ClearFromGC();
}
}
// Set Get() for details.
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
T* GetFromGC() const {
return static_cast<T*>(const_cast<void*>(GetValue()));
}
friend class internal::RootVisitor;
};
template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
typename LocationPolicy2, typename CheckingPolicy2>
bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
CheckingPolicy1>& p1,
const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
CheckingPolicy2>& p2) {
return p1.Get() == p2.Get();
}
template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
typename LocationPolicy2, typename CheckingPolicy2>
bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
CheckingPolicy1>& p1,
const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
CheckingPolicy2>& p2) {
return !(p1 == p2);
}
template <typename T1, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
typename T2, typename MemberWriteBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy>
bool operator==(
const BasicPersistent<T1, PersistentWeaknessPolicy,
PersistentLocationPolicy, PersistentCheckingPolicy>&
p,
const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
MemberCheckingPolicy>& m) {
return p.Get() == m.Get();
}
template <typename T1, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
typename T2, typename MemberWriteBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy>
bool operator!=(
const BasicPersistent<T1, PersistentWeaknessPolicy,
PersistentLocationPolicy, PersistentCheckingPolicy>&
p,
const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
MemberCheckingPolicy>& m) {
return !(p == m);
}
template <typename T1, typename MemberWriteBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename T2, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
bool operator==(
const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
MemberCheckingPolicy>& m,
const BasicPersistent<T1, PersistentWeaknessPolicy,
PersistentLocationPolicy, PersistentCheckingPolicy>&
p) {
return m.Get() == p.Get();
}
template <typename T1, typename MemberWriteBarrierPolicy,
typename MemberWeaknessTag, typename MemberCheckingPolicy,
typename T2, typename PersistentWeaknessPolicy,
typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
bool operator!=(
const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
MemberCheckingPolicy>& m,
const BasicPersistent<T1, PersistentWeaknessPolicy,
PersistentLocationPolicy, PersistentCheckingPolicy>&
p) {
return !(m == p);
}
template <typename T, typename LocationPolicy, typename CheckingPolicy>
struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
CheckingPolicy>> : std::true_type {};
} // namespace internal
/**
* Persistent is a way to create a strong pointer from an off-heap object to
* another on-heap object. As long as the Persistent handle is alive the GC will
* keep the object pointed to alive. The Persistent handle is always a GC root
* from the point of view of the GC. Persistent must be constructed and
* destructed in the same thread.
*/
template <typename T>
using Persistent =
internal::BasicPersistent<T, internal::StrongPersistentPolicy>;
/**
* WeakPersistent is a way to create a weak pointer from an off-heap object to
* an on-heap object. The pointer is automatically cleared when the pointee gets
* collected. WeakPersistent must be constructed and destructed in the same
* thread.
*/
template <typename T>
using WeakPersistent =
internal::BasicPersistent<T, internal::WeakPersistentPolicy>;
} // namespace cppgc
#endif // INCLUDE_CPPGC_PERSISTENT_H_

156
v8/include/cppgc/platform.h Normal file
View File

@ -0,0 +1,156 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_PLATFORM_H_
#define INCLUDE_CPPGC_PLATFORM_H_
#include <memory>
#include "cppgc/source-location.h"
#include "v8-platform.h" // NOLINT(build/include_directory)
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
// TODO(v8:10346): Create separate includes for concepts that are not
// V8-specific.
using IdleTask = v8::IdleTask;
using JobHandle = v8::JobHandle;
using JobDelegate = v8::JobDelegate;
using JobTask = v8::JobTask;
using PageAllocator = v8::PageAllocator;
using Task = v8::Task;
using TaskPriority = v8::TaskPriority;
using TaskRunner = v8::TaskRunner;
using TracingController = v8::TracingController;
/**
* Platform interface used by Heap. Contains allocators and executors.
*/
class V8_EXPORT Platform {
public:
virtual ~Platform() = default;
/**
* Returns the allocator used by cppgc to allocate its heap and various
* support structures.
*/
virtual PageAllocator* GetPageAllocator() = 0;
/**
* Monotonically increasing time in seconds from an arbitrary fixed point in
* the past. This function is expected to return at least
* millisecond-precision values. For this reason,
* it is recommended that the fixed point be no further in the past than
* the epoch.
**/
virtual double MonotonicallyIncreasingTime() = 0;
/**
* Foreground task runner that should be used by a Heap.
*/
virtual std::shared_ptr<TaskRunner> GetForegroundTaskRunner() {
return nullptr;
}
/**
* Posts `job_task` to run in parallel. Returns a `JobHandle` associated with
* the `Job`, which can be joined or canceled.
* This avoids degenerate cases:
* - Calling `CallOnWorkerThread()` for each work item, causing significant
* overhead.
* - Fixed number of `CallOnWorkerThread()` calls that split the work and
* might run for a long time. This is problematic when many components post
* "num cores" tasks and all expect to use all the cores. In these cases,
* the scheduler lacks context to be fair to multiple same-priority requests
* and/or ability to request lower priority work to yield when high priority
* work comes in.
* A canonical implementation of `job_task` looks like:
* \code
* class MyJobTask : public JobTask {
* public:
* MyJobTask(...) : worker_queue_(...) {}
* // JobTask implementation.
* void Run(JobDelegate* delegate) override {
* while (!delegate->ShouldYield()) {
* // Smallest unit of work.
* auto work_item = worker_queue_.TakeWorkItem(); // Thread safe.
* if (!work_item) return;
* ProcessWork(work_item);
* }
* }
*
* size_t GetMaxConcurrency() const override {
* return worker_queue_.GetSize(); // Thread safe.
* }
* };
*
* // ...
* auto handle = PostJob(TaskPriority::kUserVisible,
* std::make_unique<MyJobTask>(...));
* handle->Join();
* \endcode
*
* `PostJob()` and methods of the returned JobHandle/JobDelegate, must never
* be called while holding a lock that could be acquired by `JobTask::Run()`
* or `JobTask::GetMaxConcurrency()` -- that could result in a deadlock. This
* is because (1) `JobTask::GetMaxConcurrency()` may be invoked while holding
* internal lock (A), hence `JobTask::GetMaxConcurrency()` can only use a lock
* (B) if that lock is *never* held while calling back into `JobHandle` from
* any thread (A=>B/B=>A deadlock) and (2) `JobTask::Run()` or
* `JobTask::GetMaxConcurrency()` may be invoked synchronously from
* `JobHandle` (B=>JobHandle::foo=>B deadlock).
*
* A sufficient `PostJob()` implementation that uses the default Job provided
* in libplatform looks like:
* \code
* std::unique_ptr<JobHandle> PostJob(
* TaskPriority priority, std::unique_ptr<JobTask> job_task) override {
* return std::make_unique<DefaultJobHandle>(
* std::make_shared<DefaultJobState>(
* this, std::move(job_task), kNumThreads));
* }
* \endcode
*/
virtual std::unique_ptr<JobHandle> PostJob(
TaskPriority priority, std::unique_ptr<JobTask> job_task) {
return nullptr;
}
/**
* Returns an instance of a `TracingController`. This must be non-nullptr. The
* default implementation returns an empty `TracingController` that consumes
* trace data without effect.
*/
virtual TracingController* GetTracingController();
};
/**
* Process-global initialization of the garbage collector. Must be called before
* creating a Heap.
*
* Can be called multiple times when paired with `ShutdownProcess()`.
*
* \param page_allocator The allocator used for maintaining meta data. Must stay
* always alive and not change between multiple calls to InitializeProcess.
*/
V8_EXPORT void InitializeProcess(PageAllocator* page_allocator);
/**
* Must be called after destroying the last used heap. Some process-global
* metadata may not be returned and reused upon a subsequent
* `InitializeProcess()` call.
*/
V8_EXPORT void ShutdownProcess();
namespace internal {
V8_EXPORT void Fatal(const std::string& reason = std::string(),
const SourceLocation& = SourceLocation::Current());
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_PLATFORM_H_

View File

@ -0,0 +1,75 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_PREFINALIZER_H_
#define INCLUDE_CPPGC_PREFINALIZER_H_
#include "cppgc/internal/compiler-specific.h"
#include "cppgc/liveness-broker.h"
namespace cppgc {
namespace internal {
class V8_EXPORT PrefinalizerRegistration final {
public:
using Callback = bool (*)(const cppgc::LivenessBroker&, void*);
PrefinalizerRegistration(void*, Callback);
void* operator new(size_t, void* location) = delete;
void* operator new(size_t) = delete;
};
} // namespace internal
/**
* Macro must be used in the private section of `Class` and registers a
* prefinalization callback `void Class::PreFinalizer()`. The callback is
* invoked on garbage collection after the collector has found an object to be
* dead.
*
* Callback properties:
* - The callback is invoked before a possible destructor for the corresponding
* object.
* - The callback may access the whole object graph, irrespective of whether
* objects are considered dead or alive.
* - The callback is invoked on the same thread as the object was created on.
*
* Example:
* \code
* class WithPrefinalizer : public GarbageCollected<WithPrefinalizer> {
* CPPGC_USING_PRE_FINALIZER(WithPrefinalizer, Dispose);
*
* public:
* void Trace(Visitor*) const {}
* void Dispose() { prefinalizer_called = true; }
* ~WithPrefinalizer() {
* // prefinalizer_called == true
* }
* private:
* bool prefinalizer_called = false;
* };
* \endcode
*/
#define CPPGC_USING_PRE_FINALIZER(Class, PreFinalizer) \
public: \
static bool InvokePreFinalizer(const cppgc::LivenessBroker& liveness_broker, \
void* object) { \
static_assert(cppgc::IsGarbageCollectedOrMixinTypeV<Class>, \
"Only garbage collected objects can have prefinalizers"); \
Class* self = static_cast<Class*>(object); \
if (liveness_broker.IsHeapObjectAlive(self)) return false; \
self->PreFinalizer(); \
return true; \
} \
\
private: \
CPPGC_NO_UNIQUE_ADDRESS cppgc::internal::PrefinalizerRegistration \
prefinalizer_dummy_{this, Class::InvokePreFinalizer}; \
static_assert(true, "Force semicolon.")
} // namespace cppgc
#endif // INCLUDE_CPPGC_PREFINALIZER_H_

View File

@ -0,0 +1,36 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_
#define INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_
#include <atomic>
#include <cstddef>
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class ProcessHeapStatisticsUpdater;
} // namespace internal
class V8_EXPORT ProcessHeapStatistics final {
public:
static size_t TotalAllocatedObjectSize() {
return total_allocated_object_size_.load(std::memory_order_relaxed);
}
static size_t TotalAllocatedSpace() {
return total_allocated_space_.load(std::memory_order_relaxed);
}
private:
static std::atomic_size_t total_allocated_space_;
static std::atomic_size_t total_allocated_object_size_;
friend class internal::ProcessHeapStatisticsUpdater;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_

View File

@ -0,0 +1,32 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_SENTINEL_POINTER_H_
#define INCLUDE_CPPGC_SENTINEL_POINTER_H_
#include <cstdint>
namespace cppgc {
namespace internal {
// Special tag type used to denote some sentinel member. The semantics of the
// sentinel is defined by the embedder.
struct SentinelPointer {
static constexpr intptr_t kSentinelValue = 0b10;
template <typename T>
operator T*() const {
return reinterpret_cast<T*>(kSentinelValue);
}
// Hidden friends.
friend bool operator==(SentinelPointer, SentinelPointer) { return true; }
friend bool operator!=(SentinelPointer, SentinelPointer) { return false; }
};
} // namespace internal
constexpr internal::SentinelPointer kSentinelPointer;
} // namespace cppgc
#endif // INCLUDE_CPPGC_SENTINEL_POINTER_H_

View File

@ -0,0 +1,92 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_SOURCE_LOCATION_H_
#define INCLUDE_CPPGC_SOURCE_LOCATION_H_
#include <cstddef>
#include <string>
#include "v8config.h" // NOLINT(build/include_directory)
#if defined(__has_builtin)
#define CPPGC_SUPPORTS_SOURCE_LOCATION \
(__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \
__has_builtin(__builtin_LINE)) // NOLINT
#elif defined(V8_CC_GNU) && __GNUC__ >= 7
#define CPPGC_SUPPORTS_SOURCE_LOCATION 1
#elif defined(V8_CC_INTEL) && __ICC >= 1800
#define CPPGC_SUPPORTS_SOURCE_LOCATION 1
#else
#define CPPGC_SUPPORTS_SOURCE_LOCATION 0
#endif
namespace cppgc {
/**
* Encapsulates source location information. Mimics C++20's
* `std::source_location`.
*/
class V8_EXPORT SourceLocation final {
public:
/**
* Construct source location information corresponding to the location of the
* call site.
*/
#if CPPGC_SUPPORTS_SOURCE_LOCATION
static constexpr SourceLocation Current(
const char* function = __builtin_FUNCTION(),
const char* file = __builtin_FILE(), size_t line = __builtin_LINE()) {
return SourceLocation(function, file, line);
}
#else
static constexpr SourceLocation Current() { return SourceLocation(); }
#endif // CPPGC_SUPPORTS_SOURCE_LOCATION
/**
* Constructs unspecified source location information.
*/
constexpr SourceLocation() = default;
/**
* Returns the name of the function associated with the position represented
* by this object, if any.
*
* \returns the function name as cstring.
*/
constexpr const char* Function() const { return function_; }
/**
* Returns the name of the current source file represented by this object.
*
* \returns the file name as cstring.
*/
constexpr const char* FileName() const { return file_; }
/**
* Returns the line number represented by this object.
*
* \returns the line number.
*/
constexpr size_t Line() const { return line_; }
/**
* Returns a human-readable string representing this object.
*
* \returns a human-readable string representing source location information.
*/
std::string ToString() const;
private:
constexpr SourceLocation(const char* function, const char* file, size_t line)
: function_(function), file_(file), line_(line) {}
const char* function_ = nullptr;
const char* file_ = nullptr;
size_t line_ = 0u;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_SOURCE_LOCATION_H_

106
v8/include/cppgc/testing.h Normal file
View File

@ -0,0 +1,106 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_TESTING_H_
#define INCLUDE_CPPGC_TESTING_H_
#include "cppgc/common.h"
#include "cppgc/macros.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
class HeapHandle;
/**
* Namespace contains testing helpers.
*/
namespace testing {
/**
* Overrides the state of the stack with the provided value. Parameters passed
* to explicit garbage collection calls still take precedence. Must not be
* nested.
*
* This scope is useful to make the garbage collector consider the stack when
* tasks that invoke garbage collection (through the provided platform) contain
* interesting pointers on its stack.
*/
class V8_EXPORT V8_NODISCARD OverrideEmbedderStackStateScope final {
CPPGC_STACK_ALLOCATED();
public:
/**
* Constructs a scoped object that automatically enters and leaves the scope.
*
* \param heap_handle The corresponding heap.
*/
explicit OverrideEmbedderStackStateScope(HeapHandle& heap_handle,
EmbedderStackState state);
~OverrideEmbedderStackStateScope();
OverrideEmbedderStackStateScope(const OverrideEmbedderStackStateScope&) =
delete;
OverrideEmbedderStackStateScope& operator=(
const OverrideEmbedderStackStateScope&) = delete;
private:
HeapHandle& heap_handle_;
};
/**
* Testing interface for managed heaps that allows for controlling garbage
* collection timings. Embedders should use this class when testing the
* interaction of their code with incremental/concurrent garbage collection.
*/
class V8_EXPORT StandaloneTestingHeap final {
public:
explicit StandaloneTestingHeap(HeapHandle&);
/**
* Start an incremental garbage collection.
*/
void StartGarbageCollection();
/**
* Perform an incremental step. This will also schedule concurrent steps if
* needed.
*
* \param stack_state The state of the stack during the step.
*/
bool PerformMarkingStep(EmbedderStackState stack_state);
/**
* Finalize the current garbage collection cycle atomically.
* Assumes that garbage collection is in progress.
*
* \param stack_state The state of the stack for finalizing the garbage
* collection cycle.
*/
void FinalizeGarbageCollection(EmbedderStackState stack_state);
/**
* Toggle main thread marking on/off. Allows to stress concurrent marking
* (e.g. to better detect data races).
*
* \param should_mark Denotes whether the main thread should contribute to
* marking. Defaults to true.
*/
void ToggleMainThreadMarking(bool should_mark);
/**
* Force enable compaction for the next garbage collection cycle.
*/
void ForceCompactionForNextGarbageCollection();
private:
HeapHandle& heap_handle_;
};
V8_EXPORT bool IsHeapObjectOld(void*);
} // namespace testing
} // namespace cppgc
#endif // INCLUDE_CPPGC_TESTING_H_

View File

@ -0,0 +1,120 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_TRACE_TRAIT_H_
#define INCLUDE_CPPGC_TRACE_TRAIT_H_
#include <type_traits>
#include "cppgc/type-traits.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
class Visitor;
namespace internal {
class RootVisitor;
using TraceRootCallback = void (*)(RootVisitor&, const void* object);
// Implementation of the default TraceTrait handling GarbageCollected and
// GarbageCollectedMixin.
template <typename T,
bool =
IsGarbageCollectedMixinTypeV<typename std::remove_const<T>::type>>
struct TraceTraitImpl;
} // namespace internal
/**
* Callback for invoking tracing on a given object.
*
* \param visitor The visitor to dispatch to.
* \param object The object to invoke tracing on.
*/
using TraceCallback = void (*)(Visitor* visitor, const void* object);
/**
* Describes how to trace an object, i.e., how to visit all Oilpan-relevant
* fields of an object.
*/
struct TraceDescriptor {
/**
* Adjusted base pointer, i.e., the pointer to the class inheriting directly
* from GarbageCollected, of the object that is being traced.
*/
const void* base_object_payload;
/**
* Callback for tracing the object.
*/
TraceCallback callback;
};
namespace internal {
struct V8_EXPORT TraceTraitFromInnerAddressImpl {
static TraceDescriptor GetTraceDescriptor(const void* address);
};
/**
* Trait specifying how the garbage collector processes an object of type T.
*
* Advanced users may override handling by creating a specialization for their
* type.
*/
template <typename T>
struct TraceTraitBase {
static_assert(internal::IsTraceableV<T>, "T must have a Trace() method");
/**
* Accessor for retrieving a TraceDescriptor to process an object of type T.
*
* \param self The object to be processed.
* \returns a TraceDescriptor to process the object.
*/
static TraceDescriptor GetTraceDescriptor(const void* self) {
return internal::TraceTraitImpl<T>::GetTraceDescriptor(
static_cast<const T*>(self));
}
/**
* Function invoking the tracing for an object of type T.
*
* \param visitor The visitor to dispatch to.
* \param self The object to invoke tracing on.
*/
static void Trace(Visitor* visitor, const void* self) {
static_cast<const T*>(self)->Trace(visitor);
}
};
} // namespace internal
template <typename T>
struct TraceTrait : public internal::TraceTraitBase<T> {};
namespace internal {
template <typename T>
struct TraceTraitImpl<T, false> {
static_assert(IsGarbageCollectedTypeV<T>,
"T must be of type GarbageCollected or GarbageCollectedMixin");
static TraceDescriptor GetTraceDescriptor(const void* self) {
return {self, TraceTrait<T>::Trace};
}
};
template <typename T>
struct TraceTraitImpl<T, true> {
static TraceDescriptor GetTraceDescriptor(const void* self) {
return internal::TraceTraitFromInnerAddressImpl::GetTraceDescriptor(self);
}
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_TRACE_TRAIT_H_

View File

@ -0,0 +1,249 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_TYPE_TRAITS_H_
#define INCLUDE_CPPGC_TYPE_TRAITS_H_
// This file should stay with minimal dependencies to allow embedder to check
// against Oilpan types without including any other parts.
#include <cstddef>
#include <type_traits>
namespace cppgc {
class Visitor;
namespace internal {
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
class BasicMember;
struct DijkstraWriteBarrierPolicy;
struct NoWriteBarrierPolicy;
class StrongMemberTag;
class UntracedMemberTag;
class WeakMemberTag;
// Not supposed to be specialized by the user.
template <typename T>
struct IsWeak : std::false_type {};
// IsTraceMethodConst is used to verify that all Trace methods are marked as
// const. It is equivalent to IsTraceable but for a non-const object.
template <typename T, typename = void>
struct IsTraceMethodConst : std::false_type {};
template <typename T>
struct IsTraceMethodConst<T, std::void_t<decltype(std::declval<const T>().Trace(
std::declval<Visitor*>()))>> : std::true_type {
};
template <typename T, typename = void>
struct IsTraceable : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsTraceable<
T, std::void_t<decltype(std::declval<T>().Trace(std::declval<Visitor*>()))>>
: std::true_type {
// All Trace methods should be marked as const. If an object of type
// 'T' is traceable then any object of type 'const T' should also
// be traceable.
static_assert(IsTraceMethodConst<T>(),
"Trace methods should be marked as const.");
};
template <typename T>
constexpr bool IsTraceableV = IsTraceable<T>::value;
template <typename T, typename = void>
struct HasGarbageCollectedMixinTypeMarker : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct HasGarbageCollectedMixinTypeMarker<
T, std::void_t<
typename std::remove_const_t<T>::IsGarbageCollectedMixinTypeMarker>>
: std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T, typename = void>
struct HasGarbageCollectedTypeMarker : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct HasGarbageCollectedTypeMarker<
T,
std::void_t<typename std::remove_const_t<T>::IsGarbageCollectedTypeMarker>>
: std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T, bool = HasGarbageCollectedTypeMarker<T>::value,
bool = HasGarbageCollectedMixinTypeMarker<T>::value>
struct IsGarbageCollectedMixinType : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsGarbageCollectedMixinType<T, false, true> : std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T, bool = HasGarbageCollectedTypeMarker<T>::value>
struct IsGarbageCollectedType : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsGarbageCollectedType<T, true> : std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsGarbageCollectedOrMixinType
: std::integral_constant<bool, IsGarbageCollectedType<T>::value ||
IsGarbageCollectedMixinType<T>::value> {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T, bool = (HasGarbageCollectedTypeMarker<T>::value &&
HasGarbageCollectedMixinTypeMarker<T>::value)>
struct IsGarbageCollectedWithMixinType : std::false_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename T>
struct IsGarbageCollectedWithMixinType<T, true> : std::true_type {
static_assert(sizeof(T), "T must be fully defined");
};
template <typename BasicMemberCandidate, typename WeaknessTag,
typename WriteBarrierPolicy>
struct IsSubclassOfBasicMemberTemplate {
private:
template <typename T, typename CheckingPolicy>
static std::true_type SubclassCheck(
BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy>*);
static std::false_type SubclassCheck(...);
public:
static constexpr bool value =
decltype(SubclassCheck(std::declval<BasicMemberCandidate*>()))::value;
};
template <typename T,
bool = IsSubclassOfBasicMemberTemplate<
T, StrongMemberTag, DijkstraWriteBarrierPolicy>::value>
struct IsMemberType : std::false_type {};
template <typename T>
struct IsMemberType<T, true> : std::true_type {};
template <typename T, bool = IsSubclassOfBasicMemberTemplate<
T, WeakMemberTag, DijkstraWriteBarrierPolicy>::value>
struct IsWeakMemberType : std::false_type {};
template <typename T>
struct IsWeakMemberType<T, true> : std::true_type {};
template <typename T, bool = IsSubclassOfBasicMemberTemplate<
T, UntracedMemberTag, NoWriteBarrierPolicy>::value>
struct IsUntracedMemberType : std::false_type {};
template <typename T>
struct IsUntracedMemberType<T, true> : std::true_type {};
template <typename T>
struct IsComplete {
private:
template <typename U, size_t = sizeof(U)>
static std::true_type IsSizeOfKnown(U*);
static std::false_type IsSizeOfKnown(...);
public:
static constexpr bool value =
decltype(IsSizeOfKnown(std::declval<T*>()))::value;
};
template <typename T, typename U>
constexpr bool IsDecayedSameV =
std::is_same_v<std::decay_t<T>, std::decay_t<U>>;
template <typename B, typename D>
constexpr bool IsStrictlyBaseOfV =
std::is_base_of_v<std::decay_t<B>, std::decay_t<D>> &&
!IsDecayedSameV<B, D>;
} // namespace internal
/**
* Value is true for types that inherit from `GarbageCollectedMixin` but not
* `GarbageCollected<T>` (i.e., they are free mixins), and false otherwise.
*/
template <typename T>
constexpr bool IsGarbageCollectedMixinTypeV =
internal::IsGarbageCollectedMixinType<T>::value;
/**
* Value is true for types that inherit from `GarbageCollected<T>`, and false
* otherwise.
*/
template <typename T>
constexpr bool IsGarbageCollectedTypeV =
internal::IsGarbageCollectedType<T>::value;
/**
* Value is true for types that inherit from either `GarbageCollected<T>` or
* `GarbageCollectedMixin`, and false otherwise.
*/
template <typename T>
constexpr bool IsGarbageCollectedOrMixinTypeV =
internal::IsGarbageCollectedOrMixinType<T>::value;
/**
* Value is true for types that inherit from `GarbageCollected<T>` and
* `GarbageCollectedMixin`, and false otherwise.
*/
template <typename T>
constexpr bool IsGarbageCollectedWithMixinTypeV =
internal::IsGarbageCollectedWithMixinType<T>::value;
/**
* Value is true for types of type `Member<T>`, and false otherwise.
*/
template <typename T>
constexpr bool IsMemberTypeV = internal::IsMemberType<T>::value;
/**
* Value is true for types of type `UntracedMember<T>`, and false otherwise.
*/
template <typename T>
constexpr bool IsUntracedMemberTypeV = internal::IsUntracedMemberType<T>::value;
/**
* Value is true for types of type `WeakMember<T>`, and false otherwise.
*/
template <typename T>
constexpr bool IsWeakMemberTypeV = internal::IsWeakMemberType<T>::value;
/**
* Value is true for types that are considered weak references, and false
* otherwise.
*/
template <typename T>
constexpr bool IsWeakV = internal::IsWeak<T>::value;
/**
* Value is true for types that are complete, and false otherwise.
*/
template <typename T>
constexpr bool IsCompleteV = internal::IsComplete<T>::value;
} // namespace cppgc
#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_

411
v8/include/cppgc/visitor.h Normal file
View File

@ -0,0 +1,411 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INCLUDE_CPPGC_VISITOR_H_
#define INCLUDE_CPPGC_VISITOR_H_
#include "cppgc/custom-space.h"
#include "cppgc/ephemeron-pair.h"
#include "cppgc/garbage-collected.h"
#include "cppgc/internal/logging.h"
#include "cppgc/internal/pointer-policies.h"
#include "cppgc/liveness-broker.h"
#include "cppgc/member.h"
#include "cppgc/sentinel-pointer.h"
#include "cppgc/source-location.h"
#include "cppgc/trace-trait.h"
#include "cppgc/type-traits.h"
namespace cppgc {
namespace internal {
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
class BasicCrossThreadPersistent;
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
class BasicPersistent;
class ConservativeTracingVisitor;
class VisitorBase;
class VisitorFactory;
} // namespace internal
using WeakCallback = void (*)(const LivenessBroker&, const void*);
/**
* Visitor passed to trace methods. All managed pointers must have called the
* Visitor's trace method on them.
*
* \code
* class Foo final : public GarbageCollected<Foo> {
* public:
* void Trace(Visitor* visitor) const {
* visitor->Trace(foo_);
* visitor->Trace(weak_foo_);
* }
* private:
* Member<Foo> foo_;
* WeakMember<Foo> weak_foo_;
* };
* \endcode
*/
class V8_EXPORT Visitor {
public:
class Key {
private:
Key() = default;
friend class internal::VisitorFactory;
};
explicit Visitor(Key) {}
virtual ~Visitor() = default;
/**
* Trace method for Member.
*
* \param member Member reference retaining an object.
*/
template <typename T>
void Trace(const Member<T>& member) {
const T* value = member.GetRawAtomic();
CPPGC_DCHECK(value != kSentinelPointer);
TraceImpl(value);
}
/**
* Trace method for WeakMember.
*
* \param weak_member WeakMember reference weakly retaining an object.
*/
template <typename T>
void Trace(const WeakMember<T>& weak_member) {
static_assert(sizeof(T), "Pointee type must be fully defined.");
static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
"T must be GarbageCollected or GarbageCollectedMixin type");
static_assert(!internal::IsAllocatedOnCompactableSpace<T>::value,
"Weak references to compactable objects are not allowed");
const T* value = weak_member.GetRawAtomic();
// Bailout assumes that WeakMember emits write barrier.
if (!value) {
return;
}
CPPGC_DCHECK(value != kSentinelPointer);
VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value),
&HandleWeak<WeakMember<T>>, &weak_member);
}
/**
* Trace method for inlined objects that are not allocated themselves but
* otherwise follow managed heap layout and have a Trace() method.
*
* \param object reference of the inlined object.
*/
template <typename T>
void Trace(const T& object) {
#if V8_ENABLE_CHECKS
// This object is embedded in potentially multiple nested objects. The
// outermost object must not be in construction as such objects are (a) not
// processed immediately, and (b) only processed conservatively if not
// otherwise possible.
CheckObjectNotInConstruction(&object);
#endif // V8_ENABLE_CHECKS
TraceTrait<T>::Trace(this, &object);
}
/**
* Registers a weak callback method on the object of type T. See
* LivenessBroker for an usage example.
*
* \param object of type T specifying a weak callback method.
*/
template <typename T, void (T::*method)(const LivenessBroker&)>
void RegisterWeakCallbackMethod(const T* object) {
RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object);
}
/**
* Trace method for EphemeronPair.
*
* \param ephemeron_pair EphemeronPair reference weakly retaining a key object
* and strongly retaining a value object in case the key object is alive.
*/
template <typename K, typename V>
void Trace(const EphemeronPair<K, V>& ephemeron_pair) {
TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value);
RegisterWeakCallbackMethod<EphemeronPair<K, V>,
&EphemeronPair<K, V>::ClearValueIfKeyIsDead>(
&ephemeron_pair);
}
/**
* Trace method for a single ephemeron. Used for tracing a raw ephemeron in
* which the `key` and `value` are kept separately.
*
* \param weak_member_key WeakMember reference weakly retaining a key object.
* \param member_value Member reference with ephemeron semantics.
*/
template <typename KeyType, typename ValueType>
void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
const Member<ValueType>* member_value) {
const KeyType* key = weak_member_key.GetRawAtomic();
if (!key) return;
// `value` must always be non-null.
CPPGC_DCHECK(member_value);
const ValueType* value = member_value->GetRawAtomic();
if (!value) return;
// KeyType and ValueType may refer to GarbageCollectedMixin.
TraceDescriptor value_desc =
TraceTrait<ValueType>::GetTraceDescriptor(value);
CPPGC_DCHECK(value_desc.base_object_payload);
const void* key_base_object_payload =
TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
CPPGC_DCHECK(key_base_object_payload);
VisitEphemeron(key_base_object_payload, value, value_desc);
}
/**
* Trace method for a single ephemeron. Used for tracing a raw ephemeron in
* which the `key` and `value` are kept separately. Note that this overload
* is for non-GarbageCollected `value`s that can be traced though.
*
* \param key `WeakMember` reference weakly retaining a key object.
* \param value Reference weakly retaining a value object. Note that
* `ValueType` here should not be `Member`. It is expected that
* `TraceTrait<ValueType>::GetTraceDescriptor(value)` returns a
* `TraceDescriptor` with a null base pointer but a valid trace method.
*/
template <typename KeyType, typename ValueType>
void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
const ValueType* value) {
static_assert(!IsGarbageCollectedOrMixinTypeV<ValueType>,
"garbage-collected types must use WeakMember and Member");
const KeyType* key = weak_member_key.GetRawAtomic();
if (!key) return;
// `value` must always be non-null.
CPPGC_DCHECK(value);
TraceDescriptor value_desc =
TraceTrait<ValueType>::GetTraceDescriptor(value);
// `value_desc.base_object_payload` must be null as this override is only
// taken for non-garbage-collected values.
CPPGC_DCHECK(!value_desc.base_object_payload);
// KeyType might be a GarbageCollectedMixin.
const void* key_base_object_payload =
TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
CPPGC_DCHECK(key_base_object_payload);
VisitEphemeron(key_base_object_payload, value, value_desc);
}
/**
* Trace method that strongifies a WeakMember.
*
* \param weak_member WeakMember reference retaining an object.
*/
template <typename T>
void TraceStrongly(const WeakMember<T>& weak_member) {
const T* value = weak_member.GetRawAtomic();
CPPGC_DCHECK(value != kSentinelPointer);
TraceImpl(value);
}
/**
* Trace method for retaining containers strongly.
*
* \param object reference to the container.
*/
template <typename T>
void TraceStrongContainer(const T* object) {
TraceImpl(object);
}
/**
* Trace method for retaining containers weakly.
*
* \param object reference to the container.
* \param callback to be invoked.
* \param callback_data custom data that is passed to the callback.
*/
template <typename T>
void TraceWeakContainer(const T* object, WeakCallback callback,
const void* callback_data) {
if (!object) return;
VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object),
TraceTrait<T>::GetWeakTraceDescriptor(object), callback,
callback_data);
}
/**
* Registers a slot containing a reference to an object allocated on a
* compactable space. Such references maybe be arbitrarily moved by the GC.
*
* \param slot location of reference to object that might be moved by the GC.
* The slot must contain an uncompressed pointer.
*/
template <typename T>
void RegisterMovableReference(const T** slot) {
static_assert(internal::IsAllocatedOnCompactableSpace<T>::value,
"Only references to objects allocated on compactable spaces "
"should be registered as movable slots.");
static_assert(!IsGarbageCollectedMixinTypeV<T>,
"Mixin types do not support compaction.");
HandleMovableReference(reinterpret_cast<const void**>(slot));
}
/**
* Registers a weak callback that is invoked during garbage collection.
*
* \param callback to be invoked.
* \param data custom data that is passed to the callback.
*/
virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {}
/**
* Defers tracing an object from a concurrent thread to the mutator thread.
* Should be called by Trace methods of types that are not safe to trace
* concurrently.
*
* \param parameter tells the trace callback which object was deferred.
* \param callback to be invoked for tracing on the mutator thread.
* \param deferred_size size of deferred object.
*
* \returns false if the object does not need to be deferred (i.e. currently
* traced on the mutator thread) and true otherwise (i.e. currently traced on
* a concurrent thread).
*/
virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent(
const void* parameter, TraceCallback callback, size_t deferred_size) {
// By default tracing is not deferred.
return false;
}
protected:
virtual void Visit(const void* self, TraceDescriptor) {}
virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback,
const void* weak_member) {}
virtual void VisitEphemeron(const void* key, const void* value,
TraceDescriptor value_desc) {}
virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc,
TraceDescriptor weak_desc,
WeakCallback callback, const void* data) {}
virtual void HandleMovableReference(const void**) {}
private:
template <typename T, void (T::*method)(const LivenessBroker&)>
static void WeakCallbackMethodDelegate(const LivenessBroker& info,
const void* self) {
// Callback is registered through a potential const Trace method but needs
// to be able to modify fields. See HandleWeak.
(const_cast<T*>(static_cast<const T*>(self))->*method)(info);
}
template <typename PointerType>
static void HandleWeak(const LivenessBroker& info, const void* object) {
const PointerType* weak = static_cast<const PointerType*>(object);
auto* raw_ptr = weak->GetFromGC();
if (!info.IsHeapObjectAlive(raw_ptr)) {
weak->ClearFromGC();
}
}
template <typename T>
void TraceImpl(const T* t) {
static_assert(sizeof(T), "Pointee type must be fully defined.");
static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
"T must be GarbageCollected or GarbageCollectedMixin type");
if (!t) {
return;
}
Visit(t, TraceTrait<T>::GetTraceDescriptor(t));
}
#if V8_ENABLE_CHECKS
void CheckObjectNotInConstruction(const void* address);
#endif // V8_ENABLE_CHECKS
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
friend class internal::BasicCrossThreadPersistent;
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
typename CheckingPolicy>
friend class internal::BasicPersistent;
friend class internal::ConservativeTracingVisitor;
friend class internal::VisitorBase;
};
namespace internal {
class V8_EXPORT RootVisitor {
public:
explicit RootVisitor(Visitor::Key) {}
virtual ~RootVisitor() = default;
template <typename AnyStrongPersistentType,
std::enable_if_t<
AnyStrongPersistentType::IsStrongPersistent::value>* = nullptr>
void Trace(const AnyStrongPersistentType& p) {
using PointeeType = typename AnyStrongPersistentType::PointeeType;
const void* object = Extract(p);
if (!object) {
return;
}
VisitRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
p.Location());
}
template <typename AnyWeakPersistentType,
std::enable_if_t<
!AnyWeakPersistentType::IsStrongPersistent::value>* = nullptr>
void Trace(const AnyWeakPersistentType& p) {
using PointeeType = typename AnyWeakPersistentType::PointeeType;
static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value,
"Weak references to compactable objects are not allowed");
const void* object = Extract(p);
if (!object) {
return;
}
VisitWeakRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
&HandleWeak<AnyWeakPersistentType>, &p, p.Location());
}
protected:
virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
const void* weak_root, const SourceLocation&) {}
private:
template <typename AnyPersistentType>
static const void* Extract(AnyPersistentType& p) {
using PointeeType = typename AnyPersistentType::PointeeType;
static_assert(sizeof(PointeeType),
"Persistent's pointee type must be fully defined");
static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value,
"Persistent's pointee type must be GarbageCollected or "
"GarbageCollectedMixin");
return p.GetFromGC();
}
template <typename PointerType>
static void HandleWeak(const LivenessBroker& info, const void* object) {
const PointerType* weak = static_cast<const PointerType*>(object);
auto* raw_ptr = weak->GetFromGC();
if (!info.IsHeapObjectAlive(raw_ptr)) {
weak->ClearFromGC();
}
}
};
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_VISITOR_H_

View File

@ -0,0 +1,997 @@
{
"version": { "major": "1", "minor": "2" },
"domains": [
{
"domain": "Schema",
"description": "Provides information about the protocol schema.",
"types": [
{
"id": "Domain",
"type": "object",
"description": "Description of the protocol domain.",
"exported": true,
"properties": [
{ "name": "name", "type": "string", "description": "Domain name." },
{ "name": "version", "type": "string", "description": "Domain version." }
]
}
],
"commands": [
{
"name": "getDomains",
"description": "Returns supported domains.",
"handlers": ["browser", "renderer"],
"returns": [
{ "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." }
]
}
]
},
{
"domain": "Runtime",
"description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
"types": [
{
"id": "ScriptId",
"type": "string",
"description": "Unique script identifier."
},
{
"id": "RemoteObjectId",
"type": "string",
"description": "Unique object identifier."
},
{
"id": "UnserializableValue",
"type": "string",
"enum": ["Infinity", "NaN", "-Infinity", "-0"],
"description": "Primitive value which cannot be JSON-stringified."
},
{
"id": "RemoteObject",
"type": "object",
"description": "Mirror object referencing original JavaScript object.",
"exported": true,
"properties": [
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." },
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
{ "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
{ "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have <code>value</code>, but gets this property." },
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." },
{ "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "experimental": true },
{ "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true}
]
},
{
"id": "CustomPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "header", "type": "string"},
{ "name": "hasBody", "type": "boolean"},
{ "name": "formatterObjectId", "$ref": "RemoteObjectId"},
{ "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" },
{ "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true }
]
},
{
"id": "ObjectPreview",
"type": "object",
"experimental": true,
"description": "Object containing abbreviated remote object value.",
"properties": [
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." },
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
{ "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." },
{ "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." },
{ "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." }
]
},
{
"id": "PropertyPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "name", "type": "string", "description": "Property name." },
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." },
{ "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." },
{ "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." },
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }
]
},
{
"id": "EntryPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." },
{ "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." }
]
},
{
"id": "PropertyDescriptor",
"type": "object",
"description": "Object property descriptor.",
"properties": [
{ "name": "name", "type": "string", "description": "Property name or symbol description." },
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." },
{ "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." },
{ "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
{ "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
{ "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
{ "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
{ "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." },
{ "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type." }
]
},
{
"id": "InternalPropertyDescriptor",
"type": "object",
"description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.",
"properties": [
{ "name": "name", "type": "string", "description": "Conventional property name." },
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }
]
},
{
"id": "CallArgument",
"type": "object",
"description": "Represents function call argument. Either remote object id <code>objectId</code>, primitive <code>value</code>, unserializable primitive value or neither of (for undefined) them should be specified.",
"properties": [
{ "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
]
},
{
"id": "ExecutionContextId",
"type": "integer",
"description": "Id of an execution context."
},
{
"id": "ExecutionContextDescription",
"type": "object",
"description": "Description of an isolated world.",
"properties": [
{ "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." },
{ "name": "origin", "type": "string", "description": "Execution context origin." },
{ "name": "name", "type": "string", "description": "Human readable name describing given context." },
{ "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }
]
},
{
"id": "ExceptionDetails",
"type": "object",
"description": "Detailed information about exception (or error) that was thrown during script compilation or execution.",
"properties": [
{ "name": "exceptionId", "type": "integer", "description": "Exception id." },
{ "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." },
{ "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." },
{ "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." },
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." },
{ "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." }
]
},
{
"id": "Timestamp",
"type": "number",
"description": "Number of milliseconds since epoch."
},
{
"id": "CallFrame",
"type": "object",
"description": "Stack entry for runtime errors and assertions.",
"properties": [
{ "name": "functionName", "type": "string", "description": "JavaScript function name." },
{ "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." },
{ "name": "url", "type": "string", "description": "JavaScript script name or url." },
{ "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." },
{ "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." }
]
},
{
"id": "StackTrace",
"type": "object",
"description": "Call frames for assertions or error messages.",
"exported": true,
"properties": [
{ "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." },
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." },
{ "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." }
]
}
],
"commands": [
{
"name": "evaluate",
"async": true,
"parameters": [
{ "name": "expression", "type": "string", "description": "Expression to evaluate." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
{ "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on global object."
},
{
"name": "awaitPromise",
"async": true,
"parameters": [
{ "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."}
],
"description": "Add handler to promise with given promise object id."
},
{
"name": "callFunctionOn",
"async": true,
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." },
{ "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." },
{ "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Call result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
},
{
"name": "getProperties",
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
{ "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." },
{ "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." }
],
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." },
{ "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Returns properties of a given object. Object group of the result is inherited from the target object."
},
{
"name": "releaseObject",
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." }
],
"description": "Releases remote object with given id."
},
{
"name": "releaseObjectGroup",
"parameters": [
{ "name": "objectGroup", "type": "string", "description": "Symbolic object group name." }
],
"description": "Releases all remote objects that belong to a given group."
},
{
"name": "runIfWaitingForDebugger",
"description": "Tells inspected instance to run if it was waiting for debugger to attach."
},
{
"name": "enable",
"description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context."
},
{
"name": "disable",
"description": "Disables reporting of execution contexts creation."
},
{
"name": "discardConsoleEntries",
"description": "Discards collected exceptions and console API calls."
},
{
"name": "setCustomObjectFormatterEnabled",
"parameters": [
{
"name": "enabled",
"type": "boolean"
}
],
"experimental": true
},
{
"name": "compileScript",
"parameters": [
{ "name": "expression", "type": "string", "description": "Expression to compile." },
{ "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." },
{ "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }
],
"returns": [
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Compiles expression."
},
{
"name": "runScript",
"async": true,
"parameters": [
{ "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." },
{ "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Run result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Runs script with given id in a given context."
}
],
"events": [
{
"name": "executionContextCreated",
"parameters": [
{ "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." }
],
"description": "Issued when new execution context is created."
},
{
"name": "executionContextDestroyed",
"parameters": [
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" }
],
"description": "Issued when execution context is destroyed."
},
{
"name": "executionContextsCleared",
"description": "Issued when all executionContexts were cleared in browser"
},
{
"name": "exceptionThrown",
"description": "Issued when exception was thrown and unhandled.",
"parameters": [
{ "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails" }
]
},
{
"name": "exceptionRevoked",
"description": "Issued when unhandled exception was revoked.",
"parameters": [
{ "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." },
{ "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." }
]
},
{
"name": "consoleAPICalled",
"description": "Issued when console API was called.",
"parameters": [
{ "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Type of the call." },
{ "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." },
{ "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." }
]
},
{
"name": "inspectRequested",
"description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).",
"parameters": [
{ "name": "object", "$ref": "RemoteObject" },
{ "name": "hints", "type": "object" }
]
}
]
},
{
"domain": "Debugger",
"description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.",
"dependencies": ["Runtime"],
"types": [
{
"id": "BreakpointId",
"type": "string",
"description": "Breakpoint identifier."
},
{
"id": "CallFrameId",
"type": "string",
"description": "Call frame identifier."
},
{
"id": "Location",
"type": "object",
"properties": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." },
{ "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." },
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." }
],
"description": "Location in the source code."
},
{
"id": "ScriptPosition",
"experimental": true,
"type": "object",
"properties": [
{ "name": "lineNumber", "type": "integer" },
{ "name": "columnNumber", "type": "integer" }
],
"description": "Location in the source code."
},
{
"id": "CallFrame",
"type": "object",
"properties": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
{ "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
{ "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." },
{ "name": "location", "$ref": "Location", "description": "Location in the source code." },
{ "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
{ "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." },
{ "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." }
],
"description": "JavaScript call frame. Array of call frames form the call stack."
},
{
"id": "Scope",
"type": "object",
"properties": [
{ "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." },
{ "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." },
{ "name": "name", "type": "string", "optional": true },
{ "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" },
{ "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" }
],
"description": "Scope description."
},
{
"id": "SearchMatch",
"type": "object",
"description": "Search match for resource.",
"exported": true,
"properties": [
{ "name": "lineNumber", "type": "number", "description": "Line number in resource content." },
{ "name": "lineContent", "type": "string", "description": "Line with match content." }
],
"experimental": true
}
],
"commands": [
{
"name": "enable",
"description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received."
},
{
"name": "disable",
"description": "Disables debugger for given page."
},
{
"name": "setBreakpointsActive",
"parameters": [
{ "name": "active", "type": "boolean", "description": "New value for breakpoints active state." }
],
"description": "Activates / deactivates all breakpoints on the page."
},
{
"name": "setSkipAllPauses",
"parameters": [
{ "name": "skip", "type": "boolean", "description": "New value for skip pauses state." }
],
"description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)."
},
{
"name": "setBreakpointByUrl",
"parameters": [
{ "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." },
{ "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." },
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." },
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
],
"returns": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
{ "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." }
],
"description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads."
},
{
"name": "setBreakpoint",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." },
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
],
"returns": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
{ "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." }
],
"description": "Sets JavaScript breakpoint at a given location."
},
{
"name": "removeBreakpoint",
"parameters": [
{ "name": "breakpointId", "$ref": "BreakpointId" }
],
"description": "Removes JavaScript breakpoint."
},
{
"name": "continueToLocation",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to continue to." }
],
"description": "Continues execution until specific location is reached."
},
{
"name": "stepOver",
"description": "Steps over the statement."
},
{
"name": "stepInto",
"description": "Steps into the function call."
},
{
"name": "stepOut",
"description": "Steps out of the function call."
},
{
"name": "pause",
"description": "Stops on the next JavaScript statement."
},
{
"name": "resume",
"description": "Resumes JavaScript execution."
},
{
"name": "searchInContent",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." },
{ "name": "query", "type": "string", "description": "String to search for." },
{ "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
{ "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
],
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." }
],
"experimental": true,
"description": "Searches for given string in script content."
},
{
"name": "setScriptSource",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." },
{ "name": "scriptSource", "type": "string", "description": "New content of the script." },
{ "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." }
],
"returns": [
{ "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." },
{ "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." },
{ "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." }
],
"description": "Edits JavaScript source live."
},
{
"name": "restartFrame",
"parameters": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }
],
"returns": [
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
],
"description": "Restarts particular call frame from the beginning."
},
{
"name": "getScriptSource",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." }
],
"returns": [
{ "name": "scriptSource", "type": "string", "description": "Script source." }
],
"description": "Returns source for the script with given id."
},
{
"name": "setPauseOnExceptions",
"parameters": [
{ "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." }
],
"description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>."
},
{
"name": "evaluateOnCallFrame",
"parameters": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
{ "name": "expression", "type": "string", "description": "Expression to evaluate." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." },
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }
],
"returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
{ "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on a given call frame."
},
{
"name": "setVariableValue",
"parameters": [
{ "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." },
{ "name": "variableName", "type": "string", "description": "Variable name." },
{ "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." },
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." }
],
"description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually."
},
{
"name": "setAsyncCallStackDepth",
"parameters": [
{ "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." }
],
"description": "Enables or disables async call stacks tracking."
},
{
"name": "setBlackboxPatterns",
"parameters": [
{ "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." }
],
"experimental": true,
"description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful."
},
{
"name": "setBlackboxedRanges",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." },
{ "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } }
],
"experimental": true,
"description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted."
}
],
"events": [
{
"name": "scriptParsed",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." },
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "name": "hash", "type": "string", "description": "Content hash of the script."},
{ "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." },
{ "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true },
{ "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." },
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
],
"description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
},
{
"name": "scriptFailedToParse",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." },
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "name": "hash", "type": "string", "description": "Content hash of the script."},
{ "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." },
{ "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." },
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
],
"description": "Fired when virtual machine fails to parse the script."
},
{
"name": "breakpointResolved",
"parameters": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." },
{ "name": "location", "$ref": "Location", "description": "Actual breakpoint location." }
],
"description": "Fired when breakpoint is resolved to an actual script and location."
},
{
"name": "paused",
"parameters": [
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
{ "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason.", "exported": true },
{ "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." },
{ "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
],
"description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
},
{
"name": "resumed",
"description": "Fired when the virtual machine resumed execution."
}
]
},
{
"domain": "Console",
"description": "This domain is deprecated - use Runtime or Log instead.",
"dependencies": ["Runtime"],
"deprecated": true,
"types": [
{
"id": "ConsoleMessage",
"type": "object",
"description": "Console message.",
"properties": [
{ "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." },
{ "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." },
{ "name": "text", "type": "string", "description": "Message text." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
{ "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." },
{ "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." }
]
}
],
"commands": [
{
"name": "enable",
"description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification."
},
{
"name": "disable",
"description": "Disables console domain, prevents further console messages from being reported to the client."
},
{
"name": "clearMessages",
"description": "Does nothing."
}
],
"events": [
{
"name": "messageAdded",
"parameters": [
{ "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
],
"description": "Issued when new console message is added."
}
]
},
{
"domain": "Profiler",
"dependencies": ["Runtime", "Debugger"],
"types": [
{
"id": "ProfileNode",
"type": "object",
"description": "Profile node. Holds callsite information, execution statistics and child nodes.",
"properties": [
{ "name": "id", "type": "integer", "description": "Unique id of the node." },
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." },
{ "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." },
{ "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." }
]
},
{
"id": "Profile",
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." },
{ "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." },
{ "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." },
{ "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." },
{ "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." }
]
},
{
"id": "PositionTickInfo",
"type": "object",
"experimental": true,
"description": "Specifies a number of samples attributed to a certain source position.",
"properties": [
{ "name": "line", "type": "integer", "description": "Source line number (1-based)." },
{ "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." }
]
}
],
"commands": [
{
"name": "enable"
},
{
"name": "disable"
},
{
"name": "setSamplingInterval",
"parameters": [
{ "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." }
],
"description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started."
},
{
"name": "start"
},
{
"name": "stop",
"returns": [
{ "name": "profile", "$ref": "Profile", "description": "Recorded profile." }
]
}
],
"events": [
{
"name": "consoleProfileStarted",
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
],
"description": "Sent when new profile recodring is started using console.profile() call."
},
{
"name": "consoleProfileFinished",
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." },
{ "name": "profile", "$ref": "Profile" },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
]
}
]
},
{
"domain": "HeapProfiler",
"dependencies": ["Runtime"],
"experimental": true,
"types": [
{
"id": "HeapSnapshotObjectId",
"type": "string",
"description": "Heap snapshot object id."
},
{
"id": "SamplingHeapProfileNode",
"type": "object",
"description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.",
"properties": [
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." },
{ "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." }
]
},
{
"id": "SamplingHeapProfile",
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "head", "$ref": "SamplingHeapProfileNode" }
]
}
],
"commands": [
{
"name": "enable"
},
{
"name": "disable"
},
{
"name": "startTrackingHeapObjects",
"parameters": [
{ "name": "trackAllocations", "type": "boolean", "optional": true }
]
},
{
"name": "stopTrackingHeapObjects",
"parameters": [
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." }
]
},
{
"name": "takeHeapSnapshot",
"parameters": [
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." }
]
},
{
"name": "collectGarbage"
},
{
"name": "getObjectByHeapObjectId",
"parameters": [
{ "name": "objectId", "$ref": "HeapSnapshotObjectId" },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
],
"returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." }
]
},
{
"name": "addInspectedHeapObject",
"parameters": [
{ "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." }
],
"description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)."
},
{
"name": "getHeapObjectId",
"parameters": [
{ "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." }
],
"returns": [
{ "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." }
]
},
{
"name": "startSampling",
"parameters": [
{ "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." }
]
},
{
"name": "stopSampling",
"returns": [
{ "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." }
]
}
],
"events": [
{
"name": "addHeapSnapshotChunk",
"parameters": [
{ "name": "chunk", "type": "string" }
]
},
{
"name": "resetProfiles"
},
{
"name": "reportHeapSnapshotProgress",
"parameters": [
{ "name": "done", "type": "integer" },
{ "name": "total", "type": "integer" },
{ "name": "finished", "type": "boolean", "optional": true }
]
},
{
"name": "lastSeenObjectId",
"description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.",
"parameters": [
{ "name": "lastSeenObjectId", "type": "integer" },
{ "name": "timestamp", "type": "number" }
]
},
{
"name": "heapStatsUpdate",
"description": "If heap objects tracking has been started then backend may send update for one or more fragments",
"parameters": [
{ "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."}
]
}
]
}]
}

File diff suppressed because it is too large Load Diff

1765
v8/include/js_protocol.pdl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
include_rules = [
"+libplatform/libplatform-export.h",
]
specific_include_rules = {
"libplatform\.h": [
"+libplatform/v8-tracing.h",
],
}

View File

@ -0,0 +1,29 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_
#define V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_
#if defined(_WIN32)
#ifdef BUILDING_V8_PLATFORM_SHARED
#define V8_PLATFORM_EXPORT __declspec(dllexport)
#elif USING_V8_PLATFORM_SHARED
#define V8_PLATFORM_EXPORT __declspec(dllimport)
#else
#define V8_PLATFORM_EXPORT
#endif // BUILDING_V8_PLATFORM_SHARED
#else // defined(_WIN32)
// Setup for Linux shared library export.
#ifdef BUILDING_V8_PLATFORM_SHARED
#define V8_PLATFORM_EXPORT __attribute__((visibility("default")))
#else
#define V8_PLATFORM_EXPORT
#endif
#endif // defined(_WIN32)
#endif // V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_

View File

@ -0,0 +1,106 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_LIBPLATFORM_H_
#define V8_LIBPLATFORM_LIBPLATFORM_H_
#include <memory>
#include "libplatform/libplatform-export.h"
#include "libplatform/v8-tracing.h"
#include "v8-platform.h" // NOLINT(build/include_directory)
#include "v8config.h" // NOLINT(build/include_directory)
namespace v8 {
namespace platform {
enum class IdleTaskSupport { kDisabled, kEnabled };
enum class InProcessStackDumping { kDisabled, kEnabled };
enum class MessageLoopBehavior : bool {
kDoNotWait = false,
kWaitForWork = true
};
/**
* Returns a new instance of the default v8::Platform implementation.
*
* The caller will take ownership of the returned pointer. |thread_pool_size|
* is the number of worker threads to allocate for background jobs. If a value
* of zero is passed, a suitable default based on the current number of
* processors online will be chosen.
* If |idle_task_support| is enabled then the platform will accept idle
* tasks (IdleTasksEnabled will return true) and will rely on the embedder
* calling v8::platform::RunIdleTasks to process the idle tasks.
* If |tracing_controller| is nullptr, the default platform will create a
* v8::platform::TracingController instance and use it.
*/
V8_PLATFORM_EXPORT std::unique_ptr<v8::Platform> NewDefaultPlatform(
int thread_pool_size = 0,
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
InProcessStackDumping in_process_stack_dumping =
InProcessStackDumping::kDisabled,
std::unique_ptr<v8::TracingController> tracing_controller = {});
/**
* The same as NewDefaultPlatform but disables the worker thread pool.
* It must be used with the --single-threaded V8 flag.
*/
V8_PLATFORM_EXPORT std::unique_ptr<v8::Platform>
NewSingleThreadedDefaultPlatform(
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
InProcessStackDumping in_process_stack_dumping =
InProcessStackDumping::kDisabled,
std::unique_ptr<v8::TracingController> tracing_controller = {});
/**
* Returns a new instance of the default v8::JobHandle implementation.
*
* The job will be executed by spawning up to |num_worker_threads| many worker
* threads on the provided |platform| with the given |priority|.
*/
V8_PLATFORM_EXPORT std::unique_ptr<v8::JobHandle> NewDefaultJobHandle(
v8::Platform* platform, v8::TaskPriority priority,
std::unique_ptr<v8::JobTask> job_task, size_t num_worker_threads);
/**
* Pumps the message loop for the given isolate.
*
* The caller has to make sure that this is called from the right thread.
* Returns true if a task was executed, and false otherwise. If the call to
* PumpMessageLoop is nested within another call to PumpMessageLoop, only
* nestable tasks may run. Otherwise, any task may run. Unless requested through
* the |behavior| parameter, this call does not block if no task is pending. The
* |platform| has to be created using |NewDefaultPlatform|.
*/
V8_PLATFORM_EXPORT bool PumpMessageLoop(
v8::Platform* platform, v8::Isolate* isolate,
MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait);
/**
* Runs pending idle tasks for at most |idle_time_in_seconds| seconds.
*
* The caller has to make sure that this is called from the right thread.
* This call does not block if no task is pending. The |platform| has to be
* created using |NewDefaultPlatform|.
*/
V8_PLATFORM_EXPORT void RunIdleTasks(v8::Platform* platform,
v8::Isolate* isolate,
double idle_time_in_seconds);
/**
* Notifies the given platform about the Isolate getting deleted soon. Has to be
* called for all Isolates which are deleted - unless we're shutting down the
* platform.
*
* The |platform| has to be created using |NewDefaultPlatform|.
*
*/
V8_PLATFORM_EXPORT void NotifyIsolateShutdown(v8::Platform* platform,
Isolate* isolate);
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_LIBPLATFORM_H_

View File

@ -0,0 +1,333 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_V8_TRACING_H_
#define V8_LIBPLATFORM_V8_TRACING_H_
#include <atomic>
#include <fstream>
#include <memory>
#include <unordered_set>
#include <vector>
#include "libplatform/libplatform-export.h"
#include "v8-platform.h" // NOLINT(build/include_directory)
namespace perfetto {
namespace trace_processor {
class TraceProcessorStorage;
}
class TracingSession;
}
namespace v8 {
namespace base {
class Mutex;
} // namespace base
namespace platform {
namespace tracing {
class TraceEventListener;
const int kTraceMaxNumArgs = 2;
class V8_PLATFORM_EXPORT TraceObject {
public:
union ArgValue {
uint64_t as_uint;
int64_t as_int;
double as_double;
const void* as_pointer;
const char* as_string;
};
TraceObject() = default;
~TraceObject();
void Initialize(
char phase, const uint8_t* category_enabled_flag, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, int num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
unsigned int flags, int64_t timestamp, int64_t cpu_timestamp);
void UpdateDuration(int64_t timestamp, int64_t cpu_timestamp);
void InitializeForTesting(
char phase, const uint8_t* category_enabled_flag, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, int num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
unsigned int flags, int pid, int tid, int64_t ts, int64_t tts,
uint64_t duration, uint64_t cpu_duration);
int pid() const { return pid_; }
int tid() const { return tid_; }
char phase() const { return phase_; }
const uint8_t* category_enabled_flag() const {
return category_enabled_flag_;
}
const char* name() const { return name_; }
const char* scope() const { return scope_; }
uint64_t id() const { return id_; }
uint64_t bind_id() const { return bind_id_; }
int num_args() const { return num_args_; }
const char** arg_names() { return arg_names_; }
uint8_t* arg_types() { return arg_types_; }
ArgValue* arg_values() { return arg_values_; }
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables() {
return arg_convertables_;
}
unsigned int flags() const { return flags_; }
int64_t ts() { return ts_; }
int64_t tts() { return tts_; }
uint64_t duration() { return duration_; }
uint64_t cpu_duration() { return cpu_duration_; }
private:
int pid_;
int tid_;
char phase_;
const char* name_;
const char* scope_;
const uint8_t* category_enabled_flag_;
uint64_t id_;
uint64_t bind_id_;
int num_args_ = 0;
const char* arg_names_[kTraceMaxNumArgs];
uint8_t arg_types_[kTraceMaxNumArgs];
ArgValue arg_values_[kTraceMaxNumArgs];
std::unique_ptr<v8::ConvertableToTraceFormat>
arg_convertables_[kTraceMaxNumArgs];
char* parameter_copy_storage_ = nullptr;
unsigned int flags_;
int64_t ts_;
int64_t tts_;
uint64_t duration_;
uint64_t cpu_duration_;
// Disallow copy and assign
TraceObject(const TraceObject&) = delete;
void operator=(const TraceObject&) = delete;
};
class V8_PLATFORM_EXPORT TraceWriter {
public:
TraceWriter() = default;
virtual ~TraceWriter() = default;
virtual void AppendTraceEvent(TraceObject* trace_event) = 0;
virtual void Flush() = 0;
static TraceWriter* CreateJSONTraceWriter(std::ostream& stream);
static TraceWriter* CreateJSONTraceWriter(std::ostream& stream,
const std::string& tag);
static TraceWriter* CreateSystemInstrumentationTraceWriter();
private:
// Disallow copy and assign
TraceWriter(const TraceWriter&) = delete;
void operator=(const TraceWriter&) = delete;
};
class V8_PLATFORM_EXPORT TraceBufferChunk {
public:
explicit TraceBufferChunk(uint32_t seq);
void Reset(uint32_t new_seq);
bool IsFull() const { return next_free_ == kChunkSize; }
TraceObject* AddTraceEvent(size_t* event_index);
TraceObject* GetEventAt(size_t index) { return &chunk_[index]; }
uint32_t seq() const { return seq_; }
size_t size() const { return next_free_; }
static const size_t kChunkSize = 64;
private:
size_t next_free_ = 0;
TraceObject chunk_[kChunkSize];
uint32_t seq_;
// Disallow copy and assign
TraceBufferChunk(const TraceBufferChunk&) = delete;
void operator=(const TraceBufferChunk&) = delete;
};
class V8_PLATFORM_EXPORT TraceBuffer {
public:
TraceBuffer() = default;
virtual ~TraceBuffer() = default;
virtual TraceObject* AddTraceEvent(uint64_t* handle) = 0;
virtual TraceObject* GetEventByHandle(uint64_t handle) = 0;
virtual bool Flush() = 0;
static const size_t kRingBufferChunks = 1024;
static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks,
TraceWriter* trace_writer);
private:
// Disallow copy and assign
TraceBuffer(const TraceBuffer&) = delete;
void operator=(const TraceBuffer&) = delete;
};
// Options determines how the trace buffer stores data.
enum TraceRecordMode {
// Record until the trace buffer is full.
RECORD_UNTIL_FULL,
// Record until the user ends the trace. The trace buffer is a fixed size
// and we use it as a ring buffer during recording.
RECORD_CONTINUOUSLY,
// Record until the trace buffer is full, but with a huge buffer size.
RECORD_AS_MUCH_AS_POSSIBLE,
// Echo to console. Events are discarded.
ECHO_TO_CONSOLE,
};
class V8_PLATFORM_EXPORT TraceConfig {
public:
typedef std::vector<std::string> StringList;
static TraceConfig* CreateDefaultTraceConfig();
TraceConfig() : enable_systrace_(false), enable_argument_filter_(false) {}
TraceRecordMode GetTraceRecordMode() const { return record_mode_; }
const StringList& GetEnabledCategories() const {
return included_categories_;
}
bool IsSystraceEnabled() const { return enable_systrace_; }
bool IsArgumentFilterEnabled() const { return enable_argument_filter_; }
void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; }
void EnableSystrace() { enable_systrace_ = true; }
void EnableArgumentFilter() { enable_argument_filter_ = true; }
void AddIncludedCategory(const char* included_category);
bool IsCategoryGroupEnabled(const char* category_group) const;
private:
TraceRecordMode record_mode_;
bool enable_systrace_ : 1;
bool enable_argument_filter_ : 1;
StringList included_categories_;
// Disallow copy and assign
TraceConfig(const TraceConfig&) = delete;
void operator=(const TraceConfig&) = delete;
};
#if defined(_MSC_VER)
#define V8_PLATFORM_NON_EXPORTED_BASE(code) \
__pragma(warning(suppress : 4275)) code
#else
#define V8_PLATFORM_NON_EXPORTED_BASE(code) code
#endif // defined(_MSC_VER)
class V8_PLATFORM_EXPORT TracingController
: public V8_PLATFORM_NON_EXPORTED_BASE(v8::TracingController) {
public:
TracingController();
~TracingController() override;
#if defined(V8_USE_PERFETTO)
// Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides
// the output stream for the JSON trace data.
void InitializeForPerfetto(std::ostream* output_stream);
// Provide an optional listener for testing that will receive trace events.
// Must be called before StartTracing().
void SetTraceEventListenerForTesting(TraceEventListener* listener);
#else // defined(V8_USE_PERFETTO)
// The pointer returned from GetCategoryGroupEnabled() points to a value with
// zero or more of the following bits. Used in this class only. The
// TRACE_EVENT macros should only use the value as a bool. These values must
// be in sync with macro values in TraceEvent.h in Blink.
enum CategoryGroupEnabledFlags {
// Category group enabled for the recording mode.
ENABLED_FOR_RECORDING = 1 << 0,
// Category group enabled by SetEventCallbackEnabled().
ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
// Category group enabled to export events to ETW.
ENABLED_FOR_ETW_EXPORT = 1 << 3
};
// Takes ownership of |trace_buffer|.
void Initialize(TraceBuffer* trace_buffer);
// v8::TracingController implementation.
const uint8_t* GetCategoryGroupEnabled(const char* category_group) override;
uint64_t AddTraceEvent(
char phase, const uint8_t* category_enabled_flag, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
unsigned int flags) override;
uint64_t AddTraceEventWithTimestamp(
char phase, const uint8_t* category_enabled_flag, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
unsigned int flags, int64_t timestamp) override;
void UpdateTraceEventDuration(const uint8_t* category_enabled_flag,
const char* name, uint64_t handle) override;
static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag);
#endif // !defined(V8_USE_PERFETTO)
void AddTraceStateObserver(
v8::TracingController::TraceStateObserver* observer) override;
void RemoveTraceStateObserver(
v8::TracingController::TraceStateObserver* observer) override;
void StartTracing(TraceConfig* trace_config);
void StopTracing();
protected:
#if !defined(V8_USE_PERFETTO)
virtual int64_t CurrentTimestampMicroseconds();
virtual int64_t CurrentCpuTimestampMicroseconds();
#endif // !defined(V8_USE_PERFETTO)
private:
#if !defined(V8_USE_PERFETTO)
void UpdateCategoryGroupEnabledFlag(size_t category_index);
void UpdateCategoryGroupEnabledFlags();
#endif // !defined(V8_USE_PERFETTO)
std::unique_ptr<base::Mutex> mutex_;
std::unique_ptr<TraceConfig> trace_config_;
std::atomic_bool recording_{false};
std::unordered_set<v8::TracingController::TraceStateObserver*> observers_;
#if defined(V8_USE_PERFETTO)
std::ostream* output_stream_ = nullptr;
std::unique_ptr<perfetto::trace_processor::TraceProcessorStorage>
trace_processor_;
TraceEventListener* listener_for_testing_ = nullptr;
std::unique_ptr<perfetto::TracingSession> tracing_session_;
#else // !defined(V8_USE_PERFETTO)
std::unique_ptr<TraceBuffer> trace_buffer_;
#endif // !defined(V8_USE_PERFETTO)
// Disallow copy and assign
TracingController(const TracingController&) = delete;
void operator=(const TracingController&) = delete;
};
#undef V8_PLATFORM_NON_EXPORTED_BASE
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_V8_TRACING_H_

811
v8/include/libwebsockets.h Normal file
View File

@ -0,0 +1,811 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** @file */
#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C
#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C
#ifdef __cplusplus
#include <cstddef>
#include <cstdarg>
extern "C" {
#else
#include <stdarg.h>
#endif
#include "lws_config.h"
#if defined(LWS_HAVE_SYS_TYPES_H) && !defined(LWS_PLAT_BAREMETAL)
#include <sys/types.h>
#endif
#if defined(LWS_HAVE_NET_ETHERNET_H)
#include <net/ethernet.h>
#endif
#if defined(_WIN32) && !defined(ETHER_ADDR_LEN)
#define ETHER_ADDR_LEN 6
#endif
#define LWS_ETHER_ADDR_LEN ETHER_ADDR_LEN
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
/* place for one-shot opaque forward references */
typedef struct lws_context * lws_ctx_t;
struct lws_dsh;
/*
* CARE: everything using cmake defines needs to be below here
*/
#define LWS_US_PER_SEC ((lws_usec_t)1000000)
#define LWS_MS_PER_SEC ((lws_usec_t)1000)
#define LWS_US_PER_MS ((lws_usec_t)1000)
#define LWS_NS_PER_US ((lws_usec_t)1000)
#define LWS_KI (1024)
#define LWS_MI (LWS_KI * 1024)
#define LWS_GI (LWS_MI * 1024)
#define LWS_TI ((uint64_t)LWS_GI * 1024)
#define LWS_PI ((uint64_t)LWS_TI * 1024)
#define LWS_US_TO_MS(x) ((x + (LWS_US_PER_MS / 2)) / LWS_US_PER_MS)
#define LWS_FOURCC(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
#if defined(LWS_HAS_INTPTR_T)
#include <stdint.h>
#define lws_intptr_t intptr_t
#else
typedef unsigned long long lws_intptr_t;
#endif
#if defined(WIN32) || defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stddef.h>
#include <basetsd.h>
#include <io.h>
#ifndef _WIN32_WCE
#include <fcntl.h>
#else
#define _O_RDONLY 0x0000
#define O_RDONLY _O_RDONLY
#endif
typedef unsigned int uid_t;
typedef unsigned int gid_t;
typedef unsigned short sa_family_t;
#if !defined(LWS_HAVE_SUSECONDS_T)
typedef unsigned int useconds_t;
typedef int suseconds_t;
#endif
#define LWS_INLINE __inline
#define LWS_VISIBLE
#define LWS_WARN_UNUSED_RESULT
#define LWS_WARN_DEPRECATED
#define LWS_FORMAT(string_index)
#if !defined(LWS_EXTERN) && defined(LWS_BUILDING_SHARED)
#ifdef LWS_DLL
#ifdef LWS_INTERNAL
#define LWS_EXTERN extern __declspec(dllexport)
#else
#define LWS_EXTERN extern __declspec(dllimport)
#endif
#endif
#endif
#if !defined(LWS_INTERNAL) && !defined(LWS_EXTERN)
#define LWS_EXTERN
#define LWS_VISIBLE
#endif
#if !defined(LWS_EXTERN)
#define LWS_EXTERN
#endif
#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
#define LWS_SOCK_INVALID (INVALID_SOCKET)
#define LWS_O_RDONLY _O_RDONLY
#define LWS_O_WRONLY _O_WRONLY
#define LWS_O_CREAT _O_CREAT
#define LWS_O_TRUNC _O_TRUNC
#if (__STDC_VERSION__ < 199901L) && !defined(__func__)
#define __func__ __FUNCTION__
#endif
#else /* NOT WIN32 */
#include <unistd.h>
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
#include <sys/capability.h>
#endif
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__QNX__) || defined(__OpenBSD__) || defined(__NuttX__)
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#define LWS_INLINE inline
#define LWS_O_RDONLY O_RDONLY
#define LWS_O_WRONLY O_WRONLY
#define LWS_O_CREAT O_CREAT
#define LWS_O_TRUNC O_TRUNC
#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_TA) && !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_BAREMETAL)
#include <poll.h>
#include <netdb.h>
#define LWS_INVALID_FILE -1
#define LWS_SOCK_INVALID (-1)
#else
#define getdtablesize() (30)
#if defined(LWS_PLAT_FREERTOS)
#define LWS_INVALID_FILE NULL
#define LWS_SOCK_INVALID (-1)
#else
#define LWS_INVALID_FILE NULL
#define LWS_SOCK_INVALID (-1)
#endif
#endif
#if defined(__FreeBSD__)
#include <sys/signal.h>
#endif
#if defined(__GNUC__)
/* warn_unused_result attribute only supported by GCC 3.4 or later */
#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define LWS_WARN_UNUSED_RESULT
#endif
#if defined(LWS_BUILDING_SHARED)
/* this is only set when we're building lws itself shared */
#define LWS_VISIBLE __attribute__((visibility("default")))
#define LWS_EXTERN extern
#else /* not shared */
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
#define LWS_VISIBLE
#define LWS_EXTERN extern
#else
/*
* If we explicitly say hidden here, symbols exist as T but
* cannot be imported at link-time.
*/
#define LWS_VISIBLE
#define LWS_EXTERN
#endif
#endif /* not shared */
#define LWS_WARN_DEPRECATED __attribute__ ((deprecated))
#undef printf
#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1)))
#else /* not GNUC */
#define LWS_VISIBLE
#define LWS_WARN_UNUSED_RESULT
#define LWS_WARN_DEPRECATED
#define LWS_FORMAT(string_index)
#if !defined(LWS_EXTERN)
#define LWS_EXTERN extern
#endif
#endif
#if defined(__ANDROID__)
#include <netinet/in.h>
#include <unistd.h>
#endif
#endif
#ifdef _WIN32
#define random rand
#else
#if !defined(LWS_PLAT_OPTEE)
#include <sys/time.h>
#include <unistd.h>
#endif
#endif
#if defined(LWS_WITH_LIBUV_INTERNAL)
#include <uv.h>
#ifdef LWS_HAVE_UV_VERSION_H
#include <uv-version.h>
#endif
#ifdef LWS_HAVE_NEW_UV_VERSION_H
#include <uv/version.h>
#endif
#endif
#if defined(LWS_WITH_TLS)
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
#ifdef _WIN32
/*
* Include user-controlled settings for windows from
* <wolfssl-root>/IDE/WIN/user_settings.h
*/
#include <IDE/WIN/user_settings.h>
#include <cyassl/ctaocrypt/settings.h>
#else
#include <cyassl/options.h>
#endif
#include <cyassl/openssl/ssl.h>
#include <cyassl/error-ssl.h>
#else
#ifdef _WIN32
/*
* Include user-controlled settings for windows from
* <wolfssl-root>/IDE/WIN/user_settings.h
*/
#include <IDE/WIN/user_settings.h>
#include <wolfssl/wolfcrypt/settings.h>
#else
#include <wolfssl/options.h>
#endif
#include <wolfssl/openssl/ssl.h>
#include <wolfssl/error-ssl.h>
#endif /* not USE_OLD_CYASSL */
#else
#if defined(LWS_WITH_MBEDTLS)
#if defined(LWS_PLAT_FREERTOS)
/* this filepath is passed to us but without quotes or <> */
#if !defined(LWS_AMAZON_RTOS)
/* AMAZON RTOS has its own setting via MTK_MBEDTLS_CONFIG_FILE */
#undef MBEDTLS_CONFIG_FILE
#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
#endif
#endif
#if defined(LWS_WITH_TLS)
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/version.h>
#if !defined(MBEDTLS_PRIVATE)
#define MBEDTLS_PRIVATE(_q) _q
#endif
#if (MBEDTLS_VERSION_MAJOR == 3) && (MBEDTLS_VERSION_MINOR == 0)
#define MBEDTLS_PRIVATE_V30_ONLY(_q) MBEDTLS_PRIVATE(_q)
#else
#define MBEDTLS_PRIVATE_V30_ONLY(_q) _q
#endif
#endif
#else
#include <openssl/ssl.h>
#if !defined(LWS_WITH_MBEDTLS)
#include <openssl/err.h>
#endif
#endif
#endif /* not USE_WOLFSSL */
#endif
/*
* Helpers for pthread mutex in user code... if lws is built for
* multiple service threads, these resolve to pthread mutex
* operations. In the case LWS_MAX_SMP is 1 (the default), they
* are all NOPs and no pthread type or api is referenced.
*/
#if LWS_MAX_SMP > 1
#include <pthread.h>
#define lws_pthread_mutex(name) pthread_mutex_t name;
static LWS_INLINE void
lws_pthread_mutex_init(pthread_mutex_t *lock)
{
pthread_mutex_init(lock, NULL);
}
static LWS_INLINE void
lws_pthread_mutex_destroy(pthread_mutex_t *lock)
{
pthread_mutex_destroy(lock);
}
static LWS_INLINE void
lws_pthread_mutex_lock(pthread_mutex_t *lock)
{
pthread_mutex_lock(lock);
}
static LWS_INLINE void
lws_pthread_mutex_unlock(pthread_mutex_t *lock)
{
pthread_mutex_unlock(lock);
}
#else
#define lws_pthread_mutex(name)
#define lws_pthread_mutex_init(_a)
#define lws_pthread_mutex_destroy(_a)
#define lws_pthread_mutex_lock(_a)
#define lws_pthread_mutex_unlock(_a)
#endif
#define CONTEXT_PORT_NO_LISTEN -1
#define CONTEXT_PORT_NO_LISTEN_SERVER -2
#include <libwebsockets/lws-logs.h>
#include <stddef.h>
#ifndef lws_container_of
#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
#endif
#define LWS_ALIGN_TO(x, bou) x += ((bou) - ((x) % (bou))) % (bou)
struct lws;
/* api change list for user code to test against */
#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG
/* the struct lws_protocols has the id field present */
#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD
/* you can call lws_get_peer_write_allowance */
#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE
/* extra parameter introduced in 917f43ab821 */
#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN
/* File operations stuff exists */
#define LWS_FEATURE_FOPS
/* Mounts have extra no_cache member */
#define LWS_FEATURE_MOUNT_NO_CACHE
#if defined(_WIN32)
#if !defined(LWS_WIN32_HANDLE_TYPES)
typedef SOCKET lws_sockfd_type;
#if defined(__MINGW32__)
typedef int lws_filefd_type;
#else
typedef HANDLE lws_filefd_type;
#endif
#endif
#define lws_pollfd pollfd
#define LWS_POLLHUP (POLLHUP)
#define LWS_POLLIN (POLLRDNORM | POLLRDBAND)
#define LWS_POLLOUT (POLLWRNORM)
#else
#if defined(LWS_PLAT_FREERTOS)
#include <libwebsockets/lws-freertos.h>
#else
typedef int lws_sockfd_type;
typedef int lws_filefd_type;
#endif
#if defined(LWS_PLAT_OPTEE)
#include <time.h>
struct timeval {
time_t tv_sec;
unsigned int tv_usec;
};
#if defined(LWS_WITH_NETWORK)
// #include <poll.h>
#define lws_pollfd pollfd
struct timezone;
int gettimeofday(struct timeval *tv, struct timezone *tz);
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
typedef unsigned short sa_family_t;
typedef unsigned short in_port_t;
typedef uint32_t socklen_t;
#include <libwebsockets/lws-optee.h>
#if !defined(TEE_SE_READER_NAME_MAX)
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
#endif
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t read(int fd, void *buf, size_t count);
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
extern int errno;
uint16_t ntohs(uint16_t netshort);
uint16_t htons(uint16_t hostshort);
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
#define MSG_NOSIGNAL 0x4000
#define EAGAIN 11
#define EINTR 4
#define EWOULDBLOCK EAGAIN
#define EADDRINUSE 98
#define INADDR_ANY 0
#define AF_INET 2
#define SHUT_WR 1
#define AF_UNSPEC 0
#define PF_UNSPEC 0
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
# define AI_PASSIVE 0x0001
#define IPPROTO_UDP 17
#define SOL_SOCKET 1
#define SO_SNDBUF 7
#define EISCONN 106
#define EALREADY 114
#define EINPROGRESS 115
int shutdown(int sockfd, int how);
int close(int fd);
int atoi(const char *nptr);
long long atoll(const char *nptr);
int socket(int domain, int type, int protocol);
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
#if !defined(TEE_SE_READER_NAME_MAX)
struct lws_pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
#endif
int poll(struct pollfd *fds, int nfds, int timeout);
#define LWS_POLLHUP (0x18)
#define LWS_POLLIN (1)
#define LWS_POLLOUT (4)
#else
struct lws_pollfd;
struct sockaddr_in;
#endif
#else
#define lws_pollfd pollfd
#define LWS_POLLHUP (POLLHUP | POLLERR)
#define LWS_POLLIN (POLLIN)
#define LWS_POLLOUT (POLLOUT)
#endif
#endif
#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__)
/* ... */
#define ssize_t SSIZE_T
#endif
#if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
#include <sys/types.h>
#include <sys/stat.h>
#endif
#if defined(LWS_HAVE_STDINT_H)
#include <stdint.h>
#else
#if defined(WIN32) || defined(_WIN32)
/* !!! >:-[ */
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
#endif
#endif
typedef int64_t lws_usec_t;
typedef unsigned long long lws_filepos_t;
typedef long long lws_fileofs_t;
typedef uint32_t lws_fop_flags_t;
#define lws_concat_temp(_t, _l) (_t + sizeof(_t) - _l)
#define lws_concat_used(_t, _l) (sizeof(_t) - _l)
/** struct lws_pollargs - argument structure for all external poll related calls
* passed in via 'in' */
struct lws_pollargs {
lws_sockfd_type fd; /**< applicable socket descriptor */
int events; /**< the new event mask */
int prev_events; /**< the previous event mask */
};
#if !defined(LWS_SIZEOFPTR)
#define LWS_SIZEOFPTR ((int)sizeof (void *))
#endif
#if defined(__x86_64__)
#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */
#else
#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */
#endif
#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \
((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n))
/* last 2 is for lws-meta */
#define LWS_PRE _LWS_PAD(4 + 10 + 2)
/* used prior to 1.7 and retained for backward compatibility */
#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE
#define LWS_SEND_BUFFER_POST_PADDING 0
struct lws_extension; /* needed even with ws exts disabled for create context */
struct lws_token_limits;
struct lws_protocols;
struct lws_context;
struct lws_tokens;
struct lws_vhost;
struct lws;
/* Generic stateful operation return codes */
typedef enum {
LWS_SRET_OK = 0,
LWS_SRET_WANT_INPUT = (1 << 16),
LWS_SRET_WANT_OUTPUT = (1 << 17),
LWS_SRET_FATAL = (1 << 18),
LWS_SRET_NO_FURTHER_IN = (1 << 19),
LWS_SRET_NO_FURTHER_OUT = (1 << 20),
LWS_SRET_AWAIT_RETRY = (1 << 21),
LWS_SRET_YIELD = (1 << 22), /* return to the event loop and continue */
} lws_stateful_ret_t;
typedef struct lws_fixed3232 {
int32_t whole; /* signed 32-bit int */
int32_t frac; /* signed frac proportion from 0 to (100M - 1) */
} lws_fx_t;
#define LWS_FX_FRACTION_MSD 100000000
#define lws_neg(a) (a->whole < 0 || a->frac < 0)
#define lws_fx_set(a, x, y) { a.whole = x; a.frac = x < 0 ? -y : y; }
#define lws_fix64(a) (((int64_t)a->whole << 32) + \
(((1ll << 32) * (a->frac < 0 ? -a->frac : a->frac)) / LWS_FX_FRACTION_MSD))
#define lws_fix64_abs(a) \
((((int64_t)(a->whole < 0 ? (-a->whole) : a->whole) << 32) + \
(((1ll << 32) * (a->frac < 0 ? -a->frac : a->frac)) / LWS_FX_FRACTION_MSD)))
#define lws_fix3232(a, a64) { a->whole = (int32_t)(a64 >> 32); \
a->frac = (int32_t)((100000000 * (a64 & 0xffffffff)) >> 32); }
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_fx_add(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b);
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_fx_sub(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b);
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_fx_mul(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b);
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_fx_div(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b);
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_fx_sqrt(lws_fx_t *r, const lws_fx_t *a);
LWS_VISIBLE LWS_EXTERN int
lws_fx_comp(const lws_fx_t *a, const lws_fx_t *b);
LWS_VISIBLE LWS_EXTERN int
lws_fx_roundup(const lws_fx_t *a);
LWS_VISIBLE LWS_EXTERN int
lws_fx_rounddown(const lws_fx_t *a);
LWS_VISIBLE LWS_EXTERN const char *
lws_fx_string(const lws_fx_t *a, char *buf, size_t size);
#include <libwebsockets/lws-dll2.h>
#include <libwebsockets/lws-map.h>
#include <libwebsockets/lws-fault-injection.h>
#include <libwebsockets/lws-backtrace.h>
#include <libwebsockets/lws-timeout-timer.h>
#include <libwebsockets/lws-cache-ttl.h>
#if defined(LWS_WITH_SYS_SMD)
#include <libwebsockets/lws-smd.h>
#endif
#include <libwebsockets/lws-state.h>
#include <libwebsockets/lws-retry.h>
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-adopt.h>
#include <libwebsockets/lws-network-helper.h>
#include <libwebsockets/lws-metrics.h>
#endif
#include <libwebsockets/lws-ota.h>
#include <libwebsockets/lws-system.h>
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-ws-close.h>
#include <libwebsockets/lws-callbacks.h>
#include <libwebsockets/lws-ws-state.h>
#include <libwebsockets/lws-ws-ext.h>
#include <libwebsockets/lws-protocols-plugins.h>
#endif
#include <libwebsockets/lws-context-vhost.h>
#if defined(LWS_WITH_NETWORK)
#if defined(LWS_WITH_CONMON)
#include <libwebsockets/lws-conmon.h>
#endif
#if defined(LWS_ROLE_MQTT)
#include <libwebsockets/lws-mqtt.h>
#endif
#include <libwebsockets/lws-client.h>
#include <libwebsockets/lws-http.h>
#include <libwebsockets/lws-spa.h>
#endif
#include <libwebsockets/lws-purify.h>
#include <libwebsockets/lws-misc.h>
#include <libwebsockets/lws-dsh.h>
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-service.h>
#include <libwebsockets/lws-write.h>
#include <libwebsockets/lws-writeable.h>
#endif
#include <libwebsockets/lws-ring.h>
#include <libwebsockets/lws-sha1-base64.h>
#include <libwebsockets/lws-x509.h>
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-cgi.h>
#endif
#if defined(LWS_WITH_FILE_OPS)
#include <libwebsockets/lws-vfs.h>
#endif
#include <libwebsockets/lws-gencrypto.h>
#include <libwebsockets/lws-lejp.h>
#include <libwebsockets/lws-lecp.h>
#include <libwebsockets/lws-cose.h>
#include <libwebsockets/lws-struct.h>
#include <libwebsockets/lws-threadpool.h>
#include <libwebsockets/lws-tokenize.h>
#include <libwebsockets/lws-lwsac.h>
#include <libwebsockets/lws-fts.h>
#include <libwebsockets/lws-diskcache.h>
#include <libwebsockets/lws-secure-streams.h>
#include <libwebsockets/lws-secure-streams-serialization.h>
#include <libwebsockets/lws-secure-streams-policy.h>
#include <libwebsockets/lws-secure-streams-client.h>
#include <libwebsockets/lws-secure-streams-transport-proxy.h>
#include <libwebsockets/lws-jrpc.h>
#include <libwebsockets/lws-async-dns.h>
#if defined(LWS_WITH_TLS)
#include <libwebsockets/lws-tls-sessions.h>
#if defined(LWS_WITH_MBEDTLS)
#include <mbedtls/md5.h>
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>
#include <mbedtls/sha512.h>
#endif
#include <libwebsockets/lws-genhash.h>
#include <libwebsockets/lws-genrsa.h>
#include <libwebsockets/lws-genaes.h>
#include <libwebsockets/lws-genec.h>
#include <libwebsockets/lws-jwk.h>
#include <libwebsockets/lws-jose.h>
#include <libwebsockets/lws-jws.h>
#include <libwebsockets/lws-jwe.h>
#endif
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-eventlib-exports.h>
#endif
#include <libwebsockets/lws-i2c.h>
#include <libwebsockets/lws-spi.h>
#if defined(LWS_ESP_PLATFORM)
#include <libwebsockets/lws-esp32-spi.h>
#endif
#include <libwebsockets/lws-gpio.h>
#include <libwebsockets/lws-bb-i2c.h>
#include <libwebsockets/lws-bb-spi.h>
#include <libwebsockets/lws-button.h>
#include <libwebsockets/lws-led.h>
#include <libwebsockets/lws-pwm.h>
#include <libwebsockets/lws-upng.h>
#include <libwebsockets/lws-jpeg.h>
#include <libwebsockets/lws-display.h>
#include <libwebsockets/lws-dlo.h>
#include <libwebsockets/lws-ssd1306-i2c.h>
#include <libwebsockets/lws-ili9341-spi.h>
#include <libwebsockets/lws-spd1656-spi.h>
#include <libwebsockets/lws-uc8176-spi.h>
#include <libwebsockets/lws-ssd1675b-spi.h>
#include <libwebsockets/lws-settings.h>
#if defined(LWS_WITH_NETWORK)
#include <libwebsockets/lws-netdev.h>
#endif
#include <libwebsockets/lws-html.h>
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,279 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup sock-adopt Socket adoption helpers
* ##Socket adoption helpers
*
* When integrating with an external app with its own event loop, these can
* be used to accept connections from someone else's listening socket.
*
* When using lws own event loop, these are not needed.
*/
///@{
/**
* lws_adopt_socket() - adopt foreign socket as if listen socket accepted it
* for the default vhost of context.
*
* \param context: lws context
* \param accept_fd: fd of already-accepted socket to adopt
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* LWS adopts the socket in http serving mode, it's ready to accept an upgrade
* to ws or just serve http.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd);
/**
* lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted
* it for vhost
*
* \param vh: lws vhost
* \param accept_fd: fd of already-accepted socket to adopt
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* LWS adopts the socket in http serving mode, it's ready to accept an upgrade
* to ws or just serve http.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
typedef enum {
LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */
LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */
LWS_ADOPT_SOCKET = 2, /* flag: absent implies file */
LWS_ADOPT_ALLOW_SSL = 4, /* flag: use tls */
LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
LWS_ADOPT_FLAG_RAW_PROXY = 32, /* flag: raw proxy */
LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
} lws_adoption_type;
typedef union {
lws_sockfd_type sockfd;
lws_filefd_type filefd;
} lws_sock_file_fd_type;
#if defined(LWS_ESP_PLATFORM)
#include <lwip/sockets.h>
#endif
typedef union {
#if defined(LWS_WITH_IPV6)
struct sockaddr_in6 sa6;
#else
#if defined(LWS_ESP_PLATFORM)
uint8_t _pad_sa6[28];
#endif
#endif
struct sockaddr_in sa4;
} lws_sockaddr46;
#define sa46_sockaddr(_sa46) ((struct sockaddr *)(_sa46))
#if defined(LWS_WITH_IPV6)
#define sa46_socklen(_sa46) (socklen_t)((_sa46)->sa4.sin_family == AF_INET ? \
sizeof(struct sockaddr_in) : \
sizeof(struct sockaddr_in6))
#define sa46_sockport(_sa46, _sp) { if ((_sa46)->sa4.sin_family == AF_INET) \
(_sa46)->sa4.sin_port = (_sp); else \
(_sa46)->sa6.sin6_port = (_sp); }
#define sa46_address(_sa46) ((uint8_t *)((_sa46)->sa4.sin_family == AF_INET ? \
&_sa46->sa4.sin_addr : &_sa46->sa6.sin6_addr ))
#else
#define sa46_socklen(_sa46) (socklen_t)sizeof(struct sockaddr_in)
#define sa46_sockport(_sa46, _sp) (_sa46)->sa4.sin_port = (_sp)
#define sa46_address(_sa46) (uint8_t *)&_sa46->sa4.sin_addr
#endif
#define sa46_address_len(_sa46) ((_sa46)->sa4.sin_family == AF_INET ? 4 : 16)
#if defined(LWS_WITH_UDP)
struct lws_udp {
lws_sockaddr46 sa46;
lws_sockaddr46 sa46_pending;
uint8_t connected:1;
};
#endif
/**
* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
* if socket descriptor, should already have been accepted from listen socket
*
* \param vh: lws vhost
* \param type: OR-ed combinations of lws_adoption_type flags
* \param fd: union with either .sockfd or .filefd set
* \param vh_prot_name: NULL or vh protocol name to bind raw connection to
* \param parent: NULL or struct lws to attach new_wsi to as a child
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's
* ready to accept an upgrade to ws or just serve http.
*
* parent may be NULL, if given it should be an existing wsi that will become the
* parent of the new wsi created by this call.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lws_sock_file_fd_type fd, const char *vh_prot_name,
struct lws *parent);
typedef struct lws_adopt_desc {
struct lws_vhost *vh; /**< vhost the wsi should belong to */
lws_adoption_type type; /**< OR-ed combinations of
* lws_adoption_type flags */
lws_sock_file_fd_type fd; /**< union with either .sockfd
* or .filefd set */
const char *vh_prot_name; /**< NULL or vh protocol name to
* bind raw connection to */
struct lws *parent; /**< NULL or struct lws to
* attach new_wsi to as a child */
void *opaque; /**< opaque pointer to set on
*created wsi */
const char *fi_wsi_name; /**< NULL, or Fault Injection
* inheritence filter for
* wsi=string/ context faults */
} lws_adopt_desc_t;
/**
* lws_adopt_descriptor_vhost_via_info() - adopt foreign socket or file descriptor
* if socket descriptor, should already have been accepted from listen socket
*
* \param info: the struct containing the parameters
*
* - vh: lws vhost
* - type: OR-ed combinations of lws_adoption_type flags
* - fd: union with either .sockfd or .filefd set
* - vh_prot_name: NULL or vh protocol name to bind raw connection to
* - parent: NULL or struct lws to attach new_wsi to as a child
* - opaque: opaque pointer to set on created wsi
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's
* ready to accept an upgrade to ws or just serve http.
*
* parent may be NULL, if given it should be an existing wsi that will become the
* parent of the new wsi created by this call.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info);
/**
* lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it
* for the default vhost of context.
* \param context: lws context
* \param accept_fd: fd of already-accepted socket to adopt
* \param readbuf: NULL or pointer to data that must be drained before reading from
* accept_fd
* \param len: The length of the data held at \p readbuf
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* LWS adopts the socket in http serving mode, it's ready to accept an upgrade
* to ws or just serve http.
*
* If your external code did not already read from the socket, you can use
* lws_adopt_socket() instead.
*
* This api is guaranteed to use the data at \p readbuf first, before reading from
* the socket.
*
* \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
const char *readbuf, size_t len);
/**
* lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket
* accepted it for vhost.
* \param vhost: lws vhost
* \param accept_fd: fd of already-accepted socket to adopt
* \param readbuf: NULL or pointer to data that must be drained before reading from accept_fd
* \param len: The length of the data held at \p readbuf
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
*
* LWS adopts the socket in http serving mode, it's ready to accept an upgrade
* to ws or just serve http.
*
* If your external code did not already read from the socket, you can use
* lws_adopt_socket() instead.
*
* This api is guaranteed to use the data at \p readbuf first, before reading from
* the socket.
*
* \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
lws_sockfd_type accept_fd, const char *readbuf,
size_t len);
#define LWS_CAUDP_BIND (1 << 0)
#define LWS_CAUDP_BROADCAST (1 << 1)
#define LWS_CAUDP_PF_PACKET (1 << 2)
#if defined(LWS_WITH_UDP)
/**
* lws_create_adopt_udp() - create, bind and adopt a UDP socket
*
* \param vhost: lws vhost
* \param ads: NULL or address to do dns lookup on
* \param port: UDP port to bind to, -1 means unbound
* \param flags: 0 or LWS_CAUDP_NO_BIND
* \param protocol_name: Name of protocol on vhost to bind wsi to
* \param ifname: NULL, for network interface name to bind socket to
* \param parent_wsi: NULL or parent wsi new wsi will be a child of
* \param opaque: set created wsi opaque ptr to this
* \param retry_policy: NULL for vhost default policy else wsi specific policy
* \param fi_wsi_name: NULL, or string to inherit Fault Injection rules in
* form "wsi=string/rule". "wsi/rule" faults will be
* automatically applied as well. It's done at creation
* time so the rules can, eg, inject faults related to
* creation.
*
* Either returns new wsi bound to accept_fd, or closes accept_fd and
* returns NULL, having cleaned up any new wsi pieces.
* */
LWS_VISIBLE LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
int flags, const char *protocol_name, const char *ifname,
struct lws *parent_wsi, void *opaque,
const lws_retry_bo_t *retry_policy, const char *fi_wsi_name);
#endif
///@}

View File

@ -0,0 +1,130 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if defined(LWS_WITH_UDP) && defined(LWS_WITH_NETWORK)
typedef enum dns_query_type {
LWS_ADNS_RECORD_A = 0x01,
LWS_ADNS_RECORD_CNAME = 0x05,
LWS_ADNS_RECORD_MX = 0x0f,
LWS_ADNS_RECORD_AAAA = 0x1c,
} adns_query_type_t;
typedef enum {
LADNS_RET_FAILED_WSI_CLOSED = -4,
LADNS_RET_NXDOMAIN = -3,
LADNS_RET_TIMEDOUT = -2,
LADNS_RET_FAILED = -1,
LADNS_RET_FOUND,
LADNS_RET_CONTINUING
} lws_async_dns_retcode_t;
#define LWS_ADNS_SYNTHETIC 0x10000 /* don't send, synthetic response will
* be injected for testing */
struct addrinfo;
typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads,
const struct addrinfo *result, int n, void *opaque);
struct lws_adns_q;
struct lws_async_dns;
/**
* lws_async_dns_query() - perform a dns lookup using async dns
*
* \param context: the lws_context
* \param tsi: thread service index (usually 0)
* \param name: DNS name to look up
* \param qtype: type of query (A, AAAA etc)
* \param cb: query completion callback
* \param wsi: wsi if the query is related to one
* \param pq: NULL, or pointer to lws_adns_q query (used for testing)
*
* Starts an asynchronous DNS lookup, on completion the \p cb callback will
* be called.
*
* The reference count on the cached object is incremented for every callback
* that was called with the cached addrinfo results.
*
* The cached object can't be evicted until the reference count reaches zero...
* use lws_async_dns_freeaddrinfo() to indicate you're finsihed with the
* results for each callback that happened with them.
*/
LWS_VISIBLE LWS_EXTERN lws_async_dns_retcode_t
lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
adns_query_type_t qtype, lws_async_dns_cb_t cb,
struct lws *wsi, void *opaque, struct lws_adns_q **pq);
/**
* lws_async_dns_freeaddrinfo() - decrement refcount on cached addrinfo results
*
* \param pai: a pointert to a pointer to first addrinfo returned as result in the callback
*
* Decrements the cache object's reference count. When it reaches zero, the
* cached object may be reaped subject to LRU rules.
*
* The pointer to the first addrinfo give in the argument is set to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_async_dns_freeaddrinfo(const struct addrinfo **ai);
/**
* lws_async_dns_server_add() - add a DNS server to the lws async DNS list
*
* \param cx: the lws_context
* \param sa46: the ipv4 or ipv6 DNS server address to add
*
* Adds the given DNS server to the lws list of async DNS servers to query.
* If the address is already listed, its refcount is increased, otherwise a new
* entry is made.
*/
LWS_VISIBLE LWS_EXTERN int
lws_async_dns_server_add(struct lws_context *cx, const lws_sockaddr46 *sa46);
/**
* lws_async_dns_server_remove() - remove a DNS server from the lws async DNS list
*
* \param cx: the lws_context
* \param sa46: the ipv4 or ipv6 DNS server address to add
*
* Removes the given DNS server from the lws list of async DNS servers to query.
* If the address does not correspond to an existing entry, no action is taken.
* If it does, the refcount on it is decremented, and if it reaches zero, the
* entry is detached from the list and destroyed.
*/
LWS_VISIBLE LWS_EXTERN void
lws_async_dns_server_remove(struct lws_context *cx, const lws_sockaddr46 *sa46);
/* only needed for testing */
LWS_VISIBLE LWS_EXTERN uint16_t
lws_adns_get_tid(struct lws_adns_q *q);
LWS_VISIBLE LWS_EXTERN struct lws_async_dns *
lws_adns_get_async_dns(struct lws_adns_q *q);
LWS_VISIBLE LWS_EXTERN void
lws_adns_parse_udp(struct lws_async_dns *dns, const uint8_t *pkt, size_t len);
#endif

View File

@ -0,0 +1,280 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup lws_backtrace generic and compressed backtrace acquisition
* ##Backtrace apis
* \ingroup lwsbacktrace
*
* lws_backtrace
*
* These apis abstract acquisition and optionally compressed on binary back-
* traces, effectively build-specific signatures for where in the code you are
* and how you got there.
*/
//@{
typedef struct {
uintptr_t st[32];
uintptr_t asize;
uint8_t sp;
uint8_t pre;
uint8_t post;
} lws_backtrace_info_t;
typedef struct {
uint8_t *comp;
size_t pos;
size_t len;
} lws_backtrace_comp_t;
/*
* lws_backtrace() - init and fiull a backtrace struct
*
* \param si: the backtrace struct to populate
* \param pre: the number of call levels to snip from the top
* \param post: the number of call levels to snip from the bottom
*
* This describes the call stack into \p si. \p si doesn't need preparing
* before the call. \p pre levels of the call stack at the top will be snipped,
* this will usually want to be 1 or 2 to conceal the helpers that are making
* the call stack, such as lws_backtrace itself.
*
* \p post levels of the call stack at the bottom will be snipped, this is to
* conceal loaders or other machinery that was used to start your application,
* otherwise those entries will bloat all call stacks results on that platform.
*
* Returns 0 for success.
*/
LWS_VISIBLE LWS_EXTERN int
lws_backtrace(lws_backtrace_info_t *si, uint8_t pre, uint8_t post);
/*
* lws_backtrace_compression_stream_init() - init and fiull a backtrace struct
*
* \param c: the backtrace compression struct
* \param comp: the buffer to take the compressed bytes
* \param comp_len: the number of bytes available at \p comp
*
* This initializes the caller's lws_backtrace_comp_t. Because it's expected
* the caller will want to put his own compressed data after the compressed
* backtrace, he is responsible for the compression context.
*/
LWS_VISIBLE LWS_EXTERN void
lws_backtrace_compression_stream_init(lws_backtrace_comp_t *c,
uint8_t *comp, size_t comp_len);
/*
* lws_backtrace_compression_stream() - add bitfields to compression stream
*
* \param c: the backtrace compression context struct
* \param v: the bitfield to add to the stream
* \param bits: the number of bits of v to add
*
* This inserts bits from the LSB end of v to the compression stream.
*
* This is used by the backtrace compression, user code can use this to add
* its own bitfields into the compression stream after the compressed backtrace.
*
* User data should be added after, so that the backtrace can be processed even
* if the additional data is not understood by the processing script.
*
* Returns 0 for success or nonzero if ran out of compression output buffer.
*/
LWS_VISIBLE LWS_EXTERN int
lws_backtrace_compression_stream(lws_backtrace_comp_t *c, uintptr_t v,
unsigned int bits);
/*
* lws_backtrace_compression_destream() - add bitfields to compression stream
*
* \param c: the backtrace compression context struct
* \param _v: pointer to take the bitfield result
* \param bits: the number of bits to bring out into _v
*
* This reads the compression stream and creates a bitfield from it in \p _v.
*
* Returns 0 for success (with \p _v set to the value), or nonzero if ran out
* of compression output buffer.
*/
LWS_VISIBLE LWS_EXTERN int
lws_backtrace_compression_destream(lws_backtrace_comp_t *c, uintptr_t *_v,
unsigned int bits);
/*
* lws_backtrace_compress_backtrace() - compress backtrace si into c
*
* \param si: the backtrace struct to compress
* \param c: the backtrace compression context struct
*
* This compresses backtrace information acquired in \p si into the compression
* context \p c. It compresses first the call stack length and then each IP
* address in turn.
*
* Returns 0 for success.
*/
LWS_VISIBLE LWS_EXTERN int
lws_backtrace_compress_backtrace(lws_backtrace_info_t *si,
lws_backtrace_comp_t *c);
//@}
/** \defgroup lws_alloc_metadata helpers for allocator instrumentation
* ##Alloc Metadata APIs
* \ingroup lwsallocmetadata
*
* lws_alloc_metadata
*
* These helpers let you rapidly instrument your libc or platform memory
* allocator so that you can later dump details, including a backtrace of where
* the allocation was made, for every live heap allocation.
*
* You would use it at peak memory usage, to audit who is using what at that
* time.
*
* Effective compression is used to keep the metadata overhead to ~48 bytes
* per active allocation on 32-bit systems.
*/
//@{
/**
* lws_alloc_metadata_gen() - generate metadata blob (with compressed backtrace)
*
* \param size: the allocation size
* \param comp: buffer for compressed backtrace
* \param comp_len: number of bytes available in the compressed backtrace
* \param adj: takes the count of additional bytes needed for metadata behind
* the allocation we tell the user about
* \param cl: takes the count of bytes used in comp
*
* This helper creates the compressed part of the alloc metadata blob and
* calculates the total overallocation that is needed in \p adj.
*
* This doesn't need any locking.
*
* If \p comp_len is too small for the whole result, or it was not possible to
* get the backtrace information, the compressed part is set to empty (total
* length 2 to carry the 00 00 length).
*
* 6 or 10 (64-bit) bytes per backtrace IP allowed (currently 16) should always
* be enough, typically the compression reduces this very significantly.
*/
LWS_VISIBLE LWS_EXTERN void
lws_alloc_metadata_gen(size_t size, uint8_t *comp, size_t comp_len, size_t *adj,
size_t *cl);
/**
* _lws_alloc_metadata_adjust() - helper to inject metadata and list as active
*
* \param active: the allocation owner
* \param v: Original, true allocation pointer, adjusted on exit
* \param adj: Total size of metadata overallocation
* \param comp: The compressed metadata
* \param cl: takes the count of bytes used in comp
*
* THIS MUST BE LOCKED BY THE CALLER IF YOUR ALLOCATOR MAY BE CALLED BY OTHER
* THREADS. You can call it from an existing mutex or similar -protected
* critical section in your allocator if there is one already, or you will have
* to protect the caller of it with your own mutex so it cannot reenter.
*
* This is a helper that adjusts the allocation past the metadata part so the
* caller of the allocator using this sees what he asked for. The deallocator
* must call _lws_alloc_metadata_trim() to balance this before actual
* deallocation.
*/
LWS_VISIBLE LWS_EXTERN void
_lws_alloc_metadata_adjust(lws_dll2_owner_t *active, void **v, size_t adj, uint8_t *comp, unsigned int cl);
/**
* _lws_alloc_metadata_trim() - helper to trim metadata and remove from active
*
* \param ptr: Adjusted allocation pointer on entry, true allocation ptr on exit
* \param comp: NULL, or set on exit to point to start of compressed area
* \param complen: NULL, or set on exit to length of compressed area in bytes
*
* THIS MUST BE LOCKED BY THE CALLER IF YOUR DEALLOCATOR MAY BE CALLED BY OTHER
* THREADS. You can call it from an existing mutex or similar -protected
* critical section in your deallocator if there is one already, or you will
* have to protect that caller of it with your own mutex so it cannot reenter.
*/
LWS_VISIBLE LWS_EXTERN void
_lws_alloc_metadata_trim(void **ptr, uint8_t **comp, uint16_t *complen);
/**
* lws_alloc_metadata_parse() - parse compressed metadata into struct
*
* \param si: Struct to take the backtrace results from decompression
* \param adjusted_alloc: pointer to adjusted, user allocation start
*
* This api parses and decompresses the blob behind the \p adjusted_alloc
* address into \p si.
*
* Returns 0 for success.
*/
LWS_VISIBLE LWS_EXTERN int
lws_alloc_metadata_parse(lws_backtrace_info_t *si, const uint8_t *adjusted_alloc);
/**
* lws_alloc_metadata_dump_stdout() - helper to print base64 blob on stdout
*
* \param d: the current list item
* \param user: the optional arg given to the dump api (ignored)
*
* Generic helper that can be given to _lws_alloc_metadata_dump() as the
* callback that will emit a standardized base64 blob for the alloc metadata
*/
LWS_VISIBLE LWS_EXTERN int
lws_alloc_metadata_dump_stdout(struct lws_dll2 *d, void *user);
/**
* lws_alloc_metadata_dump_stdout() - dump all live allocs in instrumented heap
*
* \param active: the owner of the active allocation list for this heap
* \param cb: the callback to receive information
* \param arg: optional arg devivered to the callback
*
* THIS MUST BE LOCKED BY THE CALLER IF YOUR ALLOCATOR MAY BE CALLED BY OTHER
* THREADS. You can call it from an existing mutex or similar -protected
* critical section in your allocator if there is one already, or you will have
* to protect the caller of it with your own mutex so it cannot reenter.
*
* Iterates through the list of instrumented allocations calling the given
* callback for each one.
*/
LWS_VISIBLE LWS_EXTERN void
_lws_alloc_metadata_dump(lws_dll2_owner_t *active, lws_dll2_foreach_cb_t cb,
void *arg);
#if defined(LWS_WITH_ALLOC_METADATA_LWS)
/*
* Wrapper for _lws_alloc_metadata_dump() that uses the list owner that tracks
*
*/
LWS_VISIBLE LWS_EXTERN void
_lws_alloc_metadata_dump_lws(lws_dll2_foreach_cb_t cb, void *arg);
#else
#define _lws_alloc_metadata_dump_lws(_a, _b)
#endif
//@}

View File

@ -0,0 +1,66 @@
/*
* I2C - bitbanged generic gpio implementation
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for gpio, a real implementation provides
* functions for the ops that use the underlying OS gpio arrangements.
*/
typedef struct lws_bb_i2c {
lws_i2c_ops_t bb_ops; /* init to lws_bb_i2c_ops */
/* implementation-specific members */
_lws_plat_gpio_t scl;
_lws_plat_gpio_t sda;
const lws_gpio_ops_t *gpio;
void (*delay)(void);
} lws_bb_i2c_t;
#define lws_bb_i2c_ops \
{ \
.init = lws_bb_i2c_init, \
.start = lws_bb_i2c_start, \
.stop = lws_bb_i2c_stop, \
.write = lws_bb_i2c_write, \
.read = lws_bb_i2c_read, \
.set_ack = lws_bb_i2c_set_ack, \
}
int
lws_bb_i2c_init(const lws_i2c_ops_t *octx);
int
lws_bb_i2c_start(const lws_i2c_ops_t *octx);
void
lws_bb_i2c_stop(const lws_i2c_ops_t *octx);
int
lws_bb_i2c_write(const lws_i2c_ops_t *octx, uint8_t data);
int
lws_bb_i2c_read(const lws_i2c_ops_t *octx);
void
lws_bb_i2c_set_ack(const lws_i2c_ops_t *octx, int ack);

View File

@ -0,0 +1,64 @@
/*
* I2C - bitbanged generic gpio implementation
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for gpio, a real implementation provides
* functions for the ops that use the underlying OS gpio arrangements.
*/
#define LWSBBSPI_FLAG_USE_NCMD3 (1 << 7)
#define LWSBBSPI_FLAG_USE_NCMD2 (1 << 6)
#define LWSBBSPI_FLAG_USE_NCMD1 (1 << 5)
#define LWSBBSPI_FLAG_USE_NCMD0 (1 << 4)
#define LWSBBSPI_FLAG_USE_NCS3 (1 << 3)
#define LWSBBSPI_FLAG_USE_NCS2 (1 << 2)
#define LWSBBSPI_FLAG_USE_NCS1 (1 << 1)
#define LWSBBSPI_FLAG_USE_NCS0 (1 << 0)
#define LWS_SPI_BB_MAX_CH 4
typedef struct lws_bb_spi {
lws_spi_ops_t bb_ops; /* init to lws_bb_spi_ops */
/* implementation-specific members */
const lws_gpio_ops_t *gpio;
_lws_plat_gpio_t clk;
_lws_plat_gpio_t ncs[LWS_SPI_BB_MAX_CH];
_lws_plat_gpio_t ncmd[LWS_SPI_BB_MAX_CH];
_lws_plat_gpio_t mosi;
_lws_plat_gpio_t miso;
uint8_t unit;
uint8_t flags;
} lws_bb_spi_t;
#define lws_bb_spi_ops \
.init = lws_bb_spi_init, \
.queue = lws_bb_spi_queue
int
lws_bb_spi_init(const lws_spi_ops_t *octx);
int
lws_bb_spi_queue(const lws_spi_ops_t *octx, const lws_spi_desc_t *desc);

View File

@ -0,0 +1,120 @@
/*
* Generic button ops
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Leverages the lws generic gpio pieces to bind gpio buttons to smd events
*/
#if !defined(__LWS_BUTTON_H__)
#define __LWS_BUTTON_H__
typedef uint16_t lws_button_idx_t;
/* actual minimum may be 1 x RTOS tick depending on platform */
#define LWS_BUTTON_MON_TIMER_MS 5
typedef void (*lws_button_cb_t)(void *opaque, lws_button_idx_t idx, int state);
/* These are specified in ms but the granularity is LWS_BUTTON_MON_TIMER_MS,
* which may have been rounded up to an RTOS tick depending on platform */
enum {
LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK = (1 << 0)
};
typedef struct lws_button_regime {
uint16_t ms_min_down;
uint16_t ms_min_down_longpress;
uint16_t ms_up_settle;
uint16_t ms_doubleclick_grace;
uint16_t ms_repeat_down;
uint8_t flags;
/**< when double-click classification is enabled, clicks are delayed
* by ms_min_down + ms_doubleclick_grace to wait and see if it will
* become a double-click. Set LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK to
* enable it or leave that bit at 0 to get faster single-click
* classification.
*/
} lws_button_regime_t;
/*
* This is the const part of the button controller, describing the static
* bindings to gpio, and lws_smd event name information
*/
typedef struct lws_button_map {
_lws_plat_gpio_t gpio;
const char *smd_interaction_name;
const lws_button_regime_t *regime;
/**< a default regime is applied if this is left NULL */
} lws_button_map_t;
typedef struct lws_button_controller {
const char *smd_bc_name;
const lws_gpio_ops_t *gpio_ops;
const lws_button_map_t *button_map;
lws_button_idx_t active_state_bitmap;
uint8_t count_buttons;
} lws_button_controller_t;
struct lws_button_state; /* opaque */
/**
* lws_button_controller_create() - instantiate a button controller
*
* \param ctx: the lws_context
* \param controller: the static controller definition
*
* Instantiates a button controller from a static definition of the buttons
* and their smd names, and active levels, and binds it to a gpio implementation
*/
LWS_VISIBLE LWS_EXTERN struct lws_button_state *
lws_button_controller_create(struct lws_context *ctx,
const lws_button_controller_t *controller);
/**
* lws_button_controller_destroy() - destroys a button controller
*
* \param bcs: button controller state previously created
*
* Disables all buttons and then destroys and frees a previously created
* button controller.
*/
LWS_VISIBLE LWS_EXTERN void
lws_button_controller_destroy(struct lws_button_state *bcs);
LWS_VISIBLE LWS_EXTERN lws_button_idx_t
lws_button_get_bit(struct lws_button_state *bcs, const char *name);
/*
* lws_button_enable() - enable and disable buttons
*/
LWS_VISIBLE LWS_EXTERN void
lws_button_enable(struct lws_button_state *bcs,
lws_button_idx_t _reset, lws_button_idx_t _set);
#endif

View File

@ -0,0 +1,348 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup lws_cache_ttl Cache supporting expiry
* ##Cache supporting expiry
*
* These apis let you quickly and reliably implement caches of named objects,
* that have a "destroy-by date" and cache limits that will be observed.
*
* You can instantiate as many caches as you need. The first one must be an
* L1 / heap cache type, it can have parents and grandparents of other types
* which are accessible why writing / looking up and getting from the L1 cache.
* The outer "cache" layer may persistently store items to a backing store.
*
* Allocated object memory is entirely for the use of user code, up to the
* requested size.
*
* The key name for the listed objects may be any string chosen by the user,
* there is no special length limit as it is also allocated.
*
* Both expiry and LRU orderings are kept so it is easy to find out usage
* ordering and when the next object that will expire.
*
* Cached objects may be destroyed any time you go around the event loop, when
* you allocate new objects (to keep the whole cache under the specified limit),
* or when their expiry time arrives. So you shouldn't keep copies of pointers
* to cached objects after returning to the event loop.
*/
///@{
struct lws_cache_ttl_lru;
/**
* lws_cache_write_through() - add a new cache item object in all layers
*
* \param cache: the existing cache to allocate the object in
* \param specific_key: a key string that identifies the item in the cache
* \param source: optional payload for the cached item, NULL means caller will
* write the payload
* \param size: the size of the object to allocate
* \param expiry: the usec time that the object will autodestroy
* \param ppay: NULL, or a pointer to a void * to be set to the L1 payload
*
* If an item with the key already exists, it is destroyed before allocating a
* new one.
*
* Returns 0 if successful. The written entry will be scheduled to be auto-
* destroyed when \p expiry occurs.
*
* Adding or removing cache items may cause invalidation of cached queries.
*/
LWS_VISIBLE LWS_EXTERN int /* only valid until return to event loop */
lws_cache_write_through(struct lws_cache_ttl_lru *cache,
const char *specific_key, const uint8_t *source,
size_t size, lws_usec_t expiry, void **ppay);
typedef struct lws_cache_match {
lws_dll2_t list;
lws_usec_t expiry;
/* earliest expiry amongst results */
size_t payload_size;
/**< the payload is not attached here. This is a hint about what
* (*get)() will return for this tag name.
*/
size_t tag_size;
/* tag name + NUL is overcommitted */
} lws_cache_match_t;
/**
* lws_cache_heap_lookup() - get a list of matching items
*
* \param cache: the cache to search for the key
* \param wildcard_key: the item key string, may contain wildcards
* \param pdata: pointer to pointer to be set to the serialized result list
* \param psize: pointer to size_t to receive length of serialized result list
*
* This finds all unique items in the final cache that match search_key, which
* may contain wildcards. It does not return the payloads for matching items,
* just a list of specific tags in the that match.
*
* If successful, results are provided in a serialized list format, in no
* particular order, each result has the following fields
*
* - BE32: payload size in bytes (payload itself is not included)
* - BE32: specific tag name length in bytes
* - chars: tag name with terminating NUL
*
* These serialized results are themselves cached in L1 cache (only) and the
* result pointers are set pointing into that. If the results are still in L1
* cache next time this api is called, the results will be returned directly
* from that without repeating the expensive lookup on the backup store. That
* is why the results are provided in serialized form.
*
* The cached results list expiry is set to the earliest expiry of any listed
* item. Additionally any cached results are invalidated on addition or
* deletion (update is done as addition + deletion) of any item that would
* match the results' original wildcard_key. For the typical case new items
* are rare compared to lookups, this is efficient.
*
* Lookup matching does not itself affect LRU or cache status of the result
* itsems. Typically user code will get the lookup results, and then perform
* get operations on each item in its desired order, that will bring the items
* to the head of the LRU list and occupy L1 cache.
*
* Returns 0 if proceeded alright, or nonzero if error. If there was an error,
* any partial results set has been deallocated cleanly before returning.
*/
LWS_VISIBLE LWS_EXTERN int
lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key,
const void **pdata, size_t *psize);
/**
* lws_cache_item_get() - bring a specific item into L1 and get payload info
*
* \param cache: the cache to search for the key
* \param specific_key: the key string of the item to get
* \param pdata: pointer to a void * to be set to the payload in L1 cache
* \param psize: pointer to a size_t to be set to the payload size
*
* If the cache still has an item matching the key string, it will be destroyed.
*
* Adding or removing cache items may cause invalidation of cached queries.
*
* Notice the cache payload is a blob of the given size. If you are storing
* strings, there is no NUL termination unless you stored them with it.
*
* Returns 0 if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key,
const void **pdata, size_t *psize);
/**
* lws_cache_item_remove() - remove item from all cache levels
*
* \param cache: the cache to search for the key
* \param wildcard_key: the item key string
*
* Removes any copy of any item matching the \p wildcard_key from any cache
* level in one step.
*
* Adding or removing cache items may cause invalidation of cached queries
* that could refer to the removed item.
*/
LWS_VISIBLE LWS_EXTERN int
lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key);
/**
* lws_cache_footprint() - query the amount of storage used by the cache layer
*
* \param cache: cache to query
*
* Returns number of payload bytes stored in cache currently
*/
LWS_VISIBLE LWS_EXTERN uint64_t
lws_cache_footprint(struct lws_cache_ttl_lru *cache);
/**
* lws_cache_debug_dump() - if built in debug mode dump cache contents to log
*
* \param cache: cache to dump
*
* If lws was built in debug mode, dump cache to log, otherwise a NOP.
*/
LWS_VISIBLE LWS_EXTERN void
lws_cache_debug_dump(struct lws_cache_ttl_lru *cache);
typedef struct lws_cache_results {
const uint8_t *ptr; /* set before using walk api */
size_t size; /* set before using walk api */
size_t payload_len;
size_t tag_len;
const uint8_t *tag;
} lws_cache_results_t;
/**
* lws_cache_results_walk() - parse next result
*
* \param walk_ctx: the context of the results blob to walk
*
* Caller must initialize \p walk_ctx.ptr and \p walk_ctx.size before calling.
* These are set to the results returned from a _lookup api call.
*
* The call returns 0 if the struct elements have been set to a result, or 1
* if there where no more results in the blob to walk.
*
* If successful, after the call \p payload_len is set to the length of the
* payload related to this result key (the payload itself is not present),
* \p tag_len is set to the length of the result key name, and \p tag is set
* to the result tag name, with a terminating NUL.
*/
LWS_VISIBLE LWS_EXTERN int
lws_cache_results_walk(lws_cache_results_t *walk_ctx);
typedef void (*lws_cache_item_destroy_cb)(void *item, size_t size);
struct lws_cache_creation_info {
struct lws_context *cx;
/**< Mandatory: the lws_context */
const char *name;
/**< Mandatory: short cache name */
lws_cache_item_destroy_cb cb;
/**< NULL, or a callback that can hook cache item destory */
struct lws_cache_ttl_lru *parent;
/**< NULL, or next cache level */
const struct lws_cache_ops *ops;
/**< NULL for default, heap-based ops, else custom cache storage and
* query implementation */
union {
struct {
const char *filepath;
/**< the filepath to store items in */
} nscookiejar;
} u;
/**< these are extra configuration for specific cache types */
size_t max_footprint;
/**< 0, or the max heap allocation allowed before destroying
* lru items to keep it under the limit */
size_t max_items;
/**< 0, or the max number of items allowed in the cache before
* destroying lru items to keep it under the limit */
size_t max_payload;
/**< 0, or the max allowed payload size for one item */
int tsi;
/**< 0 unless using SMP, then tsi to bind sul to */
};
struct lws_cache_ops {
struct lws_cache_ttl_lru *
(*create)(const struct lws_cache_creation_info *info);
/**< create an instance of the cache type specified in info */
void
(*destroy)(struct lws_cache_ttl_lru **_cache);
/**< destroy the logical cache instance pointed to by *_cache, doesn't
* affect any NV backing storage */
int
(*expunge)(struct lws_cache_ttl_lru *cache);
/**< completely delete any backing storage related to the cache
* instance, eg, delete the backing file */
int
(*write)(struct lws_cache_ttl_lru *cache, const char *specific_key,
const uint8_t *source, size_t size, lws_usec_t expiry,
void **ppvoid);
/**< create an entry in the cache level according to the given info */
int
(*tag_match)(struct lws_cache_ttl_lru *cache, const char *wc,
const char *tag, char lookup_rules);
/**< Just tell us if tag would match wildcard, using whatever special
* rules the backing store might use for tag matching. 0 indicates
* it is a match on wildcard, nonzero means does not match.
*/
int
(*lookup)(struct lws_cache_ttl_lru *cache, const char *wildcard_key,
lws_dll2_owner_t *results_owner);
/**+ add keys for search_key matches not already listed in the results
* owner */
int
(*invalidate)(struct lws_cache_ttl_lru *cache, const char *wildcard_key);
/**< remove matching item(s) from cache level */
int
(*get)(struct lws_cache_ttl_lru *cache, const char *specific_key,
const void **pdata, size_t *psize);
/**< if it has the item, fills L1 with item. updates LRU, and returns
* pointer to payload in L1 */
void
(*debug_dump)(struct lws_cache_ttl_lru *cache);
/**< Helper to dump the whole cache contents to log, useful for debug */
};
/**
* lws_cache_create() - create an empty cache you can allocate items in
*
* \param info: a struct describing the cache to create
*
* Create an empty cache you can allocate items in. The cache will be kept
* below the max_footprint and max_items limits if they are nonzero, by
* destroying least-recently-used items until it remains below the limits.
*
* Items will auto-destroy when their expiry time is reached.
*
* When items are destroyed from the cache, if \p cb is non-NULL, it will be
* called back with the item pointer after it has been removed from the cache,
* but before it is deallocated and destroyed.
*
* context and tsi are used when scheduling expiry callbacks
*/
LWS_VISIBLE LWS_EXTERN struct lws_cache_ttl_lru *
lws_cache_create(const struct lws_cache_creation_info *info);
/**
* lws_cache_destroy() - destroy a previously created cache
*
* \param cache: pointer to the cache
*
* Everything in the cache is destroyed, then the cache itself is destroyed,
* and *cache set to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_cache_destroy(struct lws_cache_ttl_lru **cache);
/**
* lws_cache_expunge() - destroy all items in cache and parents
*
* \param cache: pointer to the cache
*
* Everything in the cache and parents is destroyed, leaving it empty.
* If the cache has a backing store, it is deleted.
*
* Returns 0 if no problems reported at any cache layer, else nonzero.
*/
LWS_VISIBLE LWS_EXTERN int
lws_cache_expunge(struct lws_cache_ttl_lru *cache);
LWS_VISIBLE extern const struct lws_cache_ops lws_cache_ops_heap,
lws_cache_ops_nscookiejar;
///@}

View File

@ -0,0 +1,920 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup usercb User Callback
*
* ##User protocol callback
*
* The protocol callback is the primary way lws interacts with
* user code. For one of a list of a few dozen reasons the callback gets
* called at some event to be handled.
*
* All of the events can be ignored, returning 0 is taken as "OK" and returning
* nonzero in most cases indicates that the connection should be closed.
*/
///@{
struct lws_ssl_info {
int where;
int ret;
};
enum lws_cert_update_state {
LWS_CUS_IDLE,
LWS_CUS_STARTING,
LWS_CUS_SUCCESS,
LWS_CUS_FAILED,
LWS_CUS_CREATE_KEYS,
LWS_CUS_REG,
LWS_CUS_AUTH,
LWS_CUS_CHALLENGE,
LWS_CUS_CREATE_REQ,
LWS_CUS_REQ,
LWS_CUS_CONFIRM,
LWS_CUS_ISSUE,
};
enum {
LWS_TLS_REQ_ELEMENT_COUNTRY,
LWS_TLS_REQ_ELEMENT_STATE,
LWS_TLS_REQ_ELEMENT_LOCALITY,
LWS_TLS_REQ_ELEMENT_ORGANIZATION,
LWS_TLS_REQ_ELEMENT_COMMON_NAME,
LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME,
LWS_TLS_REQ_ELEMENT_EMAIL,
LWS_TLS_REQ_ELEMENT_COUNT,
LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT,
LWS_TLS_SET_AUTH_PATH,
LWS_TLS_SET_CERT_PATH,
LWS_TLS_SET_KEY_PATH,
LWS_TLS_TOTAL_COUNT
};
struct lws_acme_cert_aging_args {
struct lws_vhost *vh;
const char *element_overrides[LWS_TLS_TOTAL_COUNT]; /* NULL = use pvo */
};
/*
* With LWS_CALLBACK_FILTER_NETWORK_CONNECTION callback, user_data pointer
* points to one of these
*/
struct lws_filter_network_conn_args {
struct sockaddr_storage cli_addr;
socklen_t clilen;
lws_sockfd_type accept_fd;
};
/*
* NOTE: These public enums are part of the abi. If you want to add one,
* add it at where specified so existing users are unaffected.
*/
/** enum lws_callback_reasons - reason you're getting a protocol callback */
enum lws_callback_reasons {
/* ---------------------------------------------------------------------
* ----- Callbacks related to wsi and protocol binding lifecycle -----
*/
LWS_CALLBACK_PROTOCOL_INIT = 27,
/**< One-time call per protocol, per-vhost using it, so it can
* do initial setup / allocations etc */
LWS_CALLBACK_PROTOCOL_DESTROY = 28,
/**< One-time call per protocol, per-vhost using it, indicating
* this protocol won't get used at all after this callback, the
* vhost is getting destroyed. Take the opportunity to
* deallocate everything that was allocated by the protocol. */
LWS_CALLBACK_WSI_CREATE = 29,
/**< outermost (earliest) wsi create notification to protocols[0] */
LWS_CALLBACK_WSI_DESTROY = 30,
/**< outermost (latest) wsi destroy notification to protocols[0] */
LWS_CALLBACK_WSI_TX_CREDIT_GET = 103,
/**< manually-managed connection received TX credit (len is int32) */
/* ---------------------------------------------------------------------
* ----- Callbacks related to Server TLS -----
*/
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21,
/**< if configured for
* including OpenSSL support, this callback allows your user code
* to perform extra SSL_CTX_load_verify_locations() or similar
* calls to direct OpenSSL where to find certificates the client
* can use to confirm the remote server identity. user is the
* OpenSSL SSL_CTX* */
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
/**< if configured for
* including OpenSSL support, this callback allows your user code
* to load extra certificates into the server which allow it to
* verify the validity of certificates returned by clients. user
* is the server's OpenSSL SSL_CTX* and in is the lws_vhost */
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
/**< if the libwebsockets vhost was created with the option
* LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this
* callback is generated during OpenSSL verification of the cert
* sent from the client. It is sent to protocol[0] callback as
* no protocol has been negotiated on the connection yet.
* Notice that the libwebsockets context and wsi are both NULL
* during this callback. See
* http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
* to understand more detail about the OpenSSL callback that
* generates this libwebsockets callback and the meanings of the
* arguments passed. In this callback, user is the x509_ctx,
* in is the ssl pointer and len is preverify_ok
* Notice that this callback maintains libwebsocket return
* conventions, return 0 to mean the cert is OK or 1 to fail it.
* This also means that if you don't handle this callback then
* the default callback action of returning 0 allows the client
* certificates. */
LWS_CALLBACK_SSL_INFO = 67,
/**< SSL connections only. An event you registered an
* interest in at the vhost has occurred on a connection
* using the vhost. in is a pointer to a
* struct lws_ssl_info containing information about the
* event*/
/* ---------------------------------------------------------------------
* ----- Callbacks related to Client TLS -----
*/
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58,
/**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION
* this callback is called during OpenSSL verification of the cert
* sent from the server to the client. It is sent to protocol[0]
* callback as no protocol has been negotiated on the connection yet.
* Notice that the wsi is set because lws_client_connect_via_info was
* successful.
*
* See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
* to understand more detail about the OpenSSL callback that
* generates this libwebsockets callback and the meanings of the
* arguments passed. In this callback, user is the x509_ctx,
* in is the ssl pointer and len is preverify_ok.
*
* THIS IS NOT RECOMMENDED BUT if a cert validation error shall be
* overruled and cert shall be accepted as ok,
* X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be
* called and return value must be 0 to mean the cert is OK;
* returning 1 will fail the cert in any case.
*
* This also means that if you don't handle this callback then
* the default callback action of returning 0 will not accept the
* certificate in case of a validation error decided by the SSL lib.
*
* This is expected and secure behaviour when validating certificates.
*
* Note: LCCSCF_ALLOW_SELFSIGNED and
* LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this
* callback being implemented.
*/
/* ---------------------------------------------------------------------
* ----- Callbacks related to HTTP Server -----
*/
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19,
/**< A new client has been accepted by the ws server. This
* callback allows setting any relevant property to it. Because this
* happens immediately after the instantiation of a new client,
* there's no websocket protocol selected yet so this callback is
* issued only to protocol 0. Only wsi is defined, pointing to the
* new client, and the return value is ignored. */
LWS_CALLBACK_HTTP = 12,
/**< an http request has come from a client that is not
* asking to upgrade the connection to a websocket
* one. This is a chance to serve http content,
* for example, to send a script to the client
* which will then open the websockets connection.
* in points to the URI path requested and
* lws_serve_http_file() makes it very
* simple to send back a file to the client.
* Normally after sending the file you are done
* with the http connection, since the rest of the
* activity will come by websockets from the script
* that was delivered by http, so you will want to
* return 1; to close and free up the connection. */
LWS_CALLBACK_HTTP_BODY = 13,
/**< the next len bytes data from the http
* request body HTTP connection is now available in in. */
LWS_CALLBACK_HTTP_BODY_COMPLETION = 14,
/**< the expected amount of http request body has been delivered */
LWS_CALLBACK_HTTP_FILE_COMPLETION = 15,
/**< a file requested to be sent down http link has completed. */
LWS_CALLBACK_HTTP_WRITEABLE = 16,
/**< you can write more down the http protocol link now. */
LWS_CALLBACK_CLOSED_HTTP = 5,
/**< when a HTTP (non-websocket) session ends */
LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18,
/**< called when the request has
* been received and parsed from the client, but the response is
* not sent yet. Return non-zero to disallow the connection.
* user is a pointer to the connection user space allocation,
* in is the URI, eg, "/"
* In your handler you can use the public APIs
* lws_hdr_total_length() / lws_hdr_copy() to access all of the
* headers using the header enums lws_token_indexes from
* libwebsockets.h to check for and read the supported header
* presence and content before deciding to allow the http
* connection to proceed or to kill the connection. */
LWS_CALLBACK_ADD_HEADERS = 53,
/**< This gives your user code a chance to add headers to a server
* transaction bound to your protocol. `in` points to a
* `struct lws_process_html_args` describing a buffer and length
* you can add headers into using the normal lws apis.
*
* (see LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to add headers to
* a client transaction)
*
* Only `args->p` and `args->len` are valid, and `args->p` should
* be moved on by the amount of bytes written, if any. Eg
*
* case LWS_CALLBACK_ADD_HEADERS:
*
* struct lws_process_html_args *args =
* (struct lws_process_html_args *)in;
*
* if (lws_add_http_header_by_name(wsi,
* (unsigned char *)"set-cookie:",
* (unsigned char *)cookie, cookie_len,
* (unsigned char **)&args->p,
* (unsigned char *)args->p + args->max_len))
* return 1;
*
* break;
*/
LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION = 102,
/**< This gives the user code a chance to accept or reject credentials
* provided HTTP to basic authorization. It will only be called if the
* http mount's authentication_mode is set to LWSAUTHM_BASIC_AUTH_CALLBACK
* `in` points to a credential string of the form `username:password` If
* the callback returns zero (the default if unhandled), then the
* transaction ends with HTTP_STATUS_UNAUTHORIZED, otherwise the request
* will be processed */
LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51,
/**< This gives the user code a chance to forbid an http access.
* `in` points to a `struct lws_process_html_args`, which
* describes the URL, and a bit mask describing the type of
* authentication required. If the callback returns nonzero,
* the transaction ends with HTTP_STATUS_UNAUTHORIZED. */
LWS_CALLBACK_PROCESS_HTML = 52,
/**< This gives your user code a chance to mangle outgoing
* HTML. `in` points to a `struct lws_process_html_args`
* which describes the buffer containing outgoing HTML.
* The buffer may grow up to `.max_len` (currently +128
* bytes per buffer).
*/
LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49,
/**< By default, all HTTP handling is done in protocols[0].
* However you can bind different protocols (by name) to
* different parts of the URL space using callback mounts. This
* callback occurs in the new protocol when a wsi is bound
* to that protocol. Any protocol allocation related to the
* http transaction processing should be created then.
* These specific callbacks are necessary because with HTTP/1.1,
* a single connection may perform at series of different
* transactions at different URLs, thus the lifetime of the
* protocol bind is just for one transaction, not connection. */
LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50,
/**< This is called when a transaction is unbound from a protocol.
* It indicates the connection completed its transaction and may
* do something different now. Any protocol allocation related
* to the http transaction processing should be destroyed. */
LWS_CALLBACK_HTTP_CONFIRM_UPGRADE = 86,
/**< This is your chance to reject an HTTP upgrade action. The
* name of the protocol being upgraded to is in 'in', and the ah
* is still bound to the wsi, so you can look at the headers.
*
* The default of returning 0 (ie, also if not handled) means the
* upgrade may proceed. Return <0 to just hang up the connection,
* or >0 if you have rejected the connection by returning http headers
* and response code yourself.
*
* There is no need for you to call transaction_completed() as the
* caller will take care of it when it sees you returned >0.
*/
/* ---------------------------------------------------------------------
* ----- Callbacks related to HTTP Client -----
*/
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
/**< The HTTP client connection has succeeded, and is now
* connected to the server */
LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
/**< The HTTP client connection is closing */
LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
/**< This is generated by lws_http_client_read() used to drain
* incoming data. In the case the incoming data was chunked, it will
* be split into multiple smaller callbacks for each chunk block,
* removing the chunk headers. If not chunked, it will appear all in
* one callback. */
LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
/**< This indicates data was received on the HTTP client connection. It
* does NOT actually drain or provide the data, so if you are doing
* http client, you MUST handle this and call lws_http_client_read().
* Failure to deal with it as in the minimal examples may cause spinning
* around the event loop as it's continuously signalled the same data
* is available for read. The related minimal examples show how to
* handle it.
*
* It's possible to defer calling lws_http_client_read() if you use
* rx flow control to stop further rx handling on the connection until
* you did deal with it. But normally you would call it in the handler.
*
* lws_http_client_read() strips any chunked framing and calls back
* with only payload data to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ. The
* chunking is the reason this is not just all done in one callback for
* http.
*/
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
/**< The client transaction completed... at the moment this
* is the same as closing since transaction pipelining on
* client side is not yet supported. */
LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57,
/**< when doing an HTTP type client connection, you can call
* lws_client_http_body_pending(wsi, 1) from
* LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks
* sending the HTTP headers.
*
* From this callback, when you have sent everything, you should let
* lws know by calling lws_client_http_body_pending(wsi, 0)
*/
LWS_CALLBACK_CLIENT_HTTP_REDIRECT = 104,
/**< we're handling a 3xx redirect... return nonzero to hang up */
LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL = 85,
LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL = 76,
/* ---------------------------------------------------------------------
* ----- Callbacks related to Websocket Server -----
*/
LWS_CALLBACK_ESTABLISHED = 0,
/**< (VH) after the server completes a handshake with an incoming
* client. If you built the library with ssl support, in is a
* pointer to the ssl struct associated with the connection or NULL.
*
* b0 of len is set if the connection was made using ws-over-h2
*/
LWS_CALLBACK_CLOSED = 4,
/**< when the websocket session ends */
LWS_CALLBACK_SERVER_WRITEABLE = 11,
/**< See LWS_CALLBACK_CLIENT_WRITEABLE */
LWS_CALLBACK_RECEIVE = 6,
/**< data has appeared for this server endpoint from a
* remote client, it can be found at *in and is
* len bytes long */
LWS_CALLBACK_RECEIVE_PONG = 7,
/**< servers receive PONG packets with this callback reason */
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38,
/**< The peer has sent an unsolicited Close WS packet. in and
* len are the optional close code (first 2 bytes, network
* order) and the optional additional information which is not
* defined in the standard, and may be a string or non human-readable
* data.
* If you return 0 lws will echo the close and then close the
* connection. If you return nonzero lws will just close the
* connection. */
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20,
/**< called when the handshake has
* been received and parsed from the client, but the response is
* not sent yet. Return non-zero to disallow the connection.
* user is a pointer to the connection user space allocation,
* in is the requested protocol name
* In your handler you can use the public APIs
* lws_hdr_total_length() / lws_hdr_copy() to access all of the
* headers using the header enums lws_token_indexes from
* libwebsockets.h to check for and read the supported header
* presence and content before deciding to allow the handshake
* to proceed or to kill the connection. */
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25,
/**< When the server handshake code
* sees that it does support a requested extension, before
* accepting the extension by additing to the list sent back to
* the client it gives this callback just to check that it's okay
* to use that extension. It calls back to the requested protocol
* and with in being the extension name, len is 0 and user is
* valid. Note though at this time the ESTABLISHED callback hasn't
* happened yet so if you initialize user content there, user
* content during this callback might not be useful for anything. */
LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL = 77,
LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL = 78,
/* ---------------------------------------------------------------------
* ----- Callbacks related to Websocket Client -----
*/
LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1,
/**< the request client connection has been unable to complete a
* handshake with the remote server. If in is non-NULL, you can
* find an error string of length len where it points to
*
* Diagnostic strings that may be returned include
*
* "getaddrinfo (ipv6) failed"
* "unknown address family"
* "getaddrinfo (ipv4) failed"
* "set socket opts failed"
* "insert wsi failed"
* "lws_ssl_client_connect1 failed"
* "lws_ssl_client_connect2 failed"
* "Peer hung up"
* "read failed"
* "HS: URI missing"
* "HS: Redirect code but no Location"
* "HS: URI did not parse"
* "HS: Redirect failed"
* "HS: Server did not return 200"
* "HS: OOM"
* "HS: disallowed by client filter"
* "HS: disallowed at ESTABLISHED"
* "HS: ACCEPT missing"
* "HS: ws upgrade response not 101"
* "HS: UPGRADE missing"
* "HS: Upgrade to something other than websocket"
* "HS: CONNECTION missing"
* "HS: UPGRADE malformed"
* "HS: PROTOCOL malformed"
* "HS: Cannot match protocol"
* "HS: EXT: list too big"
* "HS: EXT: failed setting defaults"
* "HS: EXT: failed parsing defaults"
* "HS: EXT: failed parsing options"
* "HS: EXT: Rejects server options"
* "HS: EXT: unknown ext"
* "HS: Accept hash wrong"
* "HS: Rejected by filter cb"
* "HS: OOM"
* "HS: SO_SNDBUF failed"
* "HS: Rejected at CLIENT_ESTABLISHED"
*/
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2,
/**< this is the last chance for the client user code to examine the
* http headers and decide to reject the connection. If the
* content in the headers is interesting to the
* client (url, etc) it needs to copy it out at
* this point since it will be destroyed before
* the CLIENT_ESTABLISHED call */
LWS_CALLBACK_CLIENT_ESTABLISHED = 3,
/**< after your client connection completed the websocket upgrade
* handshake with the remote server */
LWS_CALLBACK_CLIENT_CLOSED = 75,
/**< when a client websocket session ends */
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24,
/**< this callback happens
* when a client handshake is being compiled. user is NULL,
* in is a char **, it's pointing to a char * which holds the
* next location in the header buffer where you can add
* headers, and len is the remaining space in the header buffer,
* which is typically some hundreds of bytes. So, to add a canned
* cookie, your handler code might look similar to:
*
* char **p = (char **)in, *end = (*p) + len;
*
* if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE,
* (unsigned char)"a=b", 3, p, end))
* return -1;
*
* See LWS_CALLBACK_ADD_HEADERS for adding headers to server
* transactions.
*/
LWS_CALLBACK_CLIENT_RECEIVE = 8,
/**< data has appeared from the server for the client connection, it
* can be found at *in and is len bytes long */
LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9,
/**< clients receive PONG packets with this callback reason */
LWS_CALLBACK_CLIENT_WRITEABLE = 10,
/**< If you call lws_callback_on_writable() on a connection, you will
* get one of these callbacks coming when the connection socket
* is able to accept another write packet without blocking.
* If it already was able to take another packet without blocking,
* you'll get this callback at the next call to the service loop
* function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
* and servers get LWS_CALLBACK_SERVER_WRITEABLE. */
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26,
/**< When a ws client
* connection is being prepared to start a handshake to a server,
* each supported extension is checked with protocols[0] callback
* with this reason, giving the user code a chance to suppress the
* claim to support that extension by returning non-zero. If
* unhandled, by default 0 will be returned and the extension
* support included in the header to the server. Notice this
* callback comes to protocols[0]. */
LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
/**< Gives client connections an opportunity to adjust negotiated
* extension defaults. `user` is the extension name that was
* negotiated (eg, "permessage-deflate"). `in` points to a
* buffer and `len` is the buffer size. The user callback can
* set the buffer to a string describing options the extension
* should parse. Or just ignore for defaults. */
LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17,
/**< called when a client connects to
* the server at network level; the connection is accepted but then
* passed to this callback to decide whether to hang up immediately
* or not, based on the client IP.
*
* user_data in the callback points to a
* struct lws_filter_network_conn_args that is prepared with the
* sockfd, and the peer's address information.
*
* in contains the connection socket's descriptor.
*
* Since the client connection information is not available yet,
* wsi still pointing to the main server socket.
*
* Return non-zero to terminate the connection before sending or
* receiving anything. Because this happens immediately after the
* network connection from the client, there's no websocket protocol
* selected yet so this callback is issued only to protocol 0. */
LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL = 79,
LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL = 80,
/* ---------------------------------------------------------------------
* ----- Callbacks related to external poll loop integration -----
*/
LWS_CALLBACK_GET_THREAD_ID = 31,
/**< lws can accept callback when writable requests from other
* threads, if you implement this callback and return an opaque
* current thread ID integer. */
/* external poll() management support */
LWS_CALLBACK_ADD_POLL_FD = 32,
/**< lws normally deals with its poll() or other event loop
* internally, but in the case you are integrating with another
* server you will need to have lws sockets share a
* polling array with the other server. This and the other
* POLL_FD related callbacks let you put your specialized
* poll array interface code in the callback for protocol 0, the
* first protocol you support, usually the HTTP protocol in the
* serving case.
* This callback happens when a socket needs to be
* added to the polling loop: in points to a struct
* lws_pollargs; the fd member of the struct is the file
* descriptor, and events contains the active events
*
* If you are using the internal lws polling / event loop
* you can just ignore these callbacks. */
LWS_CALLBACK_DEL_POLL_FD = 33,
/**< This callback happens when a socket descriptor
* needs to be removed from an external polling array. in is
* again the struct lws_pollargs containing the fd member
* to be removed. If you are using the internal polling
* loop, you can just ignore it. */
LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34,
/**< This callback happens when lws wants to modify the events for
* a connection.
* in is the struct lws_pollargs with the fd to change.
* The new event mask is in events member and the old mask is in
* the prev_events member.
* If you are using the internal polling loop, you can just ignore
* it. */
LWS_CALLBACK_LOCK_POLL = 35,
/**< These allow the external poll changes driven
* by lws to participate in an external thread locking
* scheme around the changes, so the whole thing is threadsafe.
* These are called around three activities in the library,
* - inserting a new wsi in the wsi / fd table (len=1)
* - deleting a wsi from the wsi / fd table (len=1)
* - changing a wsi's POLLIN/OUT state (len=0)
* Locking and unlocking external synchronization objects when
* len == 1 allows external threads to be synchronized against
* wsi lifecycle changes if it acquires the same lock for the
* duration of wsi dereference from the other thread context. */
LWS_CALLBACK_UNLOCK_POLL = 36,
/**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */
/* ---------------------------------------------------------------------
* ----- Callbacks related to CGI serving -----
*/
LWS_CALLBACK_CGI = 40,
/**< CGI: CGI IO events on stdin / out / err are sent here on
* protocols[0]. The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. */
LWS_CALLBACK_CGI_TERMINATED = 41,
/**< CGI: The related CGI process ended, this is called before
* the wsi is closed. Used to, eg, terminate chunking.
* The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. The child PID that terminated is in len. */
LWS_CALLBACK_CGI_STDIN_DATA = 42,
/**< CGI: Data is, to be sent to the CGI process stdin, eg from
* a POST body. The provided `lws_callback_http_dummy()`
* handles this and the callback should be directed there if
* you use CGI. */
LWS_CALLBACK_CGI_STDIN_COMPLETED = 43,
/**< CGI: no more stdin is coming. The provided
* `lws_callback_http_dummy()` handles this and the callback
* should be directed there if you use CGI. */
LWS_CALLBACK_CGI_PROCESS_ATTACH = 70,
/**< CGI: Sent when the CGI process is spawned for the wsi. The
* len parameter is the PID of the child process */
/* ---------------------------------------------------------------------
* ----- Callbacks related to Generic Sessions -----
*/
LWS_CALLBACK_SESSION_INFO = 54,
/**< This is only generated by user code using generic sessions.
* It's used to get a `struct lws_session_info` filled in by
* generic sessions with information about the logged-in user.
* See the messageboard sample for an example of how to use. */
LWS_CALLBACK_GS_EVENT = 55,
/**< Indicates an event happened to the Generic Sessions session.
* `in` contains a `struct lws_gs_event_args` describing the event. */
LWS_CALLBACK_HTTP_PMO = 56,
/**< per-mount options for this connection, called before
* the normal LWS_CALLBACK_HTTP when the mount has per-mount
* options.
*/
/* ---------------------------------------------------------------------
* ----- Callbacks related to RAW PROXY -----
*/
LWS_CALLBACK_RAW_PROXY_CLI_RX = 89,
/**< RAW mode client (outgoing) RX */
LWS_CALLBACK_RAW_PROXY_SRV_RX = 90,
/**< RAW mode server (listening) RX */
LWS_CALLBACK_RAW_PROXY_CLI_CLOSE = 91,
/**< RAW mode client (outgoing) is closing */
LWS_CALLBACK_RAW_PROXY_SRV_CLOSE = 92,
/**< RAW mode server (listening) is closing */
LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE = 93,
/**< RAW mode client (outgoing) may be written */
LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE = 94,
/**< RAW mode server (listening) may be written */
LWS_CALLBACK_RAW_PROXY_CLI_ADOPT = 95,
/**< RAW mode client (onward) accepted socket was adopted
* (equivalent to 'wsi created') */
LWS_CALLBACK_RAW_PROXY_SRV_ADOPT = 96,
/**< RAW mode server (listening) accepted socket was adopted
* (equivalent to 'wsi created') */
LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL = 97,
LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL = 98,
LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL = 99,
LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL = 100,
/* ---------------------------------------------------------------------
* ----- Callbacks related to RAW sockets -----
*/
LWS_CALLBACK_RAW_RX = 59,
/**< RAW mode connection RX */
LWS_CALLBACK_RAW_CLOSE = 60,
/**< RAW mode connection is closing */
LWS_CALLBACK_RAW_WRITEABLE = 61,
/**< RAW mode connection may be written */
LWS_CALLBACK_RAW_ADOPT = 62,
/**< RAW mode connection was adopted (equivalent to 'wsi created') */
LWS_CALLBACK_RAW_CONNECTED = 101,
/**< outgoing client RAW mode connection was connected */
LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL = 81,
LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL = 82,
/* ---------------------------------------------------------------------
* ----- Callbacks related to RAW file handles -----
*/
LWS_CALLBACK_RAW_ADOPT_FILE = 63,
/**< RAW mode file was adopted (equivalent to 'wsi created') */
LWS_CALLBACK_RAW_RX_FILE = 64,
/**< This is the indication the RAW mode file has something to read.
* This doesn't actually do the read of the file and len is always
* 0... your code should do the read having been informed there is
* something to read now. */
LWS_CALLBACK_RAW_WRITEABLE_FILE = 65,
/**< RAW mode file is writeable */
LWS_CALLBACK_RAW_CLOSE_FILE = 66,
/**< RAW mode wsi that adopted a file is closing */
LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL = 83,
LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL = 84,
/* ---------------------------------------------------------------------
* ----- Callbacks related to generic wsi events -----
*/
LWS_CALLBACK_TIMER = 73,
/**< When the time elapsed after a call to
* lws_set_timer_usecs(wsi, usecs) is up, the wsi will get one of
* these callbacks. The deadline can be continuously extended into the
* future by later calls to lws_set_timer_usecs() before the deadline
* expires, or cancelled by lws_set_timer_usecs(wsi, -1);
*/
LWS_CALLBACK_EVENT_WAIT_CANCELLED = 71,
/**< This is sent to every protocol of every vhost in response
* to lws_cancel_service() or lws_cancel_service_pt(). This
* callback is serialized in the lws event loop normally, even
* if the lws_cancel_service[_pt]() call was from a different
* thread. */
LWS_CALLBACK_CHILD_CLOSING = 69,
/**< Sent to parent to notify them a child is closing / being
* destroyed. in is the child wsi.
*/
LWS_CALLBACK_CONNECTING = 105,
/**< Called before a socketfd is about to connect(). In is the
* socketfd, cast to a (void *), if on a platform where the socketfd
* is an int, recover portably using (lws_sockfd_type)(intptr_t)in.
*
* It's also called in SOCKS5 or http_proxy cases where the socketfd is
* going to try to connect to its proxy.
*/
/* ---------------------------------------------------------------------
* ----- Callbacks related to TLS certificate management -----
*/
LWS_CALLBACK_VHOST_CERT_AGING = 72,
/**< When a vhost TLS cert has its expiry checked, this callback
* is broadcast to every protocol of every vhost in case the
* protocol wants to take some action with this information.
* \p in is a pointer to a struct lws_acme_cert_aging_args,
* and \p len is the number of days left before it expires, as
* a (ssize_t). In the struct lws_acme_cert_aging_args, vh
* points to the vhost the cert aging information applies to,
* and element_overrides[] is an optional way to update information
* from the pvos... NULL in an index means use the information from
* from the pvo for the cert renewal, non-NULL in the array index
* means use that pointer instead for the index. */
LWS_CALLBACK_VHOST_CERT_UPDATE = 74,
/**< When a vhost TLS cert is being updated, progress is
* reported to the vhost in question here, including completion
* and failure. in points to optional JSON, and len represents the
* connection state using enum lws_cert_update_state */
/* ---------------------------------------------------------------------
* ----- Callbacks related to MQTT Client -----
*/
LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED = 200,
LWS_CALLBACK_MQTT_IDLE = 201,
LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED = 202,
LWS_CALLBACK_MQTT_SUBSCRIBED = 203,
LWS_CALLBACK_MQTT_CLIENT_WRITEABLE = 204,
LWS_CALLBACK_MQTT_CLIENT_RX = 205,
LWS_CALLBACK_MQTT_UNSUBSCRIBED = 206,
LWS_CALLBACK_MQTT_DROP_PROTOCOL = 207,
LWS_CALLBACK_MQTT_CLIENT_CLOSED = 208,
LWS_CALLBACK_MQTT_ACK = 209,
/**< When a message is fully sent, if QoS0 this callback is generated
* to locally "acknowledge" it. For QoS1, this callback is only
* generated when the matching PUBACK is received. Return nonzero to
* close the wsi.
*/
LWS_CALLBACK_MQTT_RESEND = 210,
/**< In QoS1 or QoS2, this callback is generated instead of the _ACK one
* if we timed out waiting for a PUBACK or a PUBREC, and we must resend
* the message. Return nonzero to close the wsi.
*/
LWS_CALLBACK_MQTT_UNSUBSCRIBE_TIMEOUT = 211,
/**< When a UNSUBSCRIBE is sent, this callback is generated instead of
* the _UNSUBSCRIBED one if we timed out waiting for a UNSUBACK.
* Return nonzero to close the wsi.
*/
LWS_CALLBACK_MQTT_SHADOW_TIMEOUT = 212,
/**< When a Device Shadow is sent, this callback is generated if we
* timed out waiting for a response from AWS IoT.
* Return nonzero to close the wsi.
*/
/****** add new things just above ---^ ******/
LWS_CALLBACK_USER = 1000,
/**< user code can use any including above without fear of clashes */
};
/**
* typedef lws_callback_function() - User server actions
* \param wsi: Opaque websocket instance pointer
* \param reason: The reason for the call
* \param user: Pointer to per-session user data allocated by library
* \param in: Pointer used for some callback reasons
* \param len: Length set for some callback reasons
*
* This callback is the way the user controls what is served. All the
* protocol detail is hidden and handled by the library.
*
* For each connection / session there is user data allocated that is
* pointed to by "user". You set the size of this user data area when
* the library is initialized with lws_create_server.
*/
typedef int
lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
#define LWS_CB_REASON_AUX_BF__CGI 1
#define LWS_CB_REASON_AUX_BF__PROXY 2
#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4
#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8
#define LWS_CB_REASON_AUX_BF__PROXY_TRANS_END 16
#define LWS_CB_REASON_AUX_BF__PROXY_HEADERS 32
///@}

View File

@ -0,0 +1,104 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup cgi cgi handling
*
* ##CGI handling
*
* These functions allow low-level control over stdin/out/err of the cgi.
*
* However for most cases, binding the cgi to http in and out, the default
* lws implementation already does the right thing.
*/
enum lws_enum_stdinouterr {
LWS_STDIN = 0,
LWS_STDOUT = 1,
LWS_STDERR = 2,
};
enum lws_cgi_hdr_state {
LCHS_HEADER,
LCHS_CR1,
LCHS_LF1,
LCHS_CR2,
LCHS_LF2,
LHCS_RESPONSE,
LHCS_DUMP_HEADERS,
LHCS_PAYLOAD,
LCHS_SINGLE_0A,
};
struct lws_cgi_args {
struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */
enum lws_enum_stdinouterr ch; /**< channel index */
unsigned char *data; /**< for messages with payload */
enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */
int len; /**< length */
};
#ifdef LWS_WITH_CGI
/**
* lws_cgi: spawn network-connected cgi process
*
* \param wsi: connection to own the process
* \param exec_array: array of "exec-name" "arg1" ... "argn" NULL
* \param script_uri_path_len: how many chars on the left of the uri are the
* path to the cgi, or -1 to spawn without URL-related env vars
* \param timeout_secs: seconds script should be allowed to run
* \param mp_cgienv: pvo list with per-vhost cgi options to put in env
*/
LWS_VISIBLE LWS_EXTERN int
lws_cgi(struct lws *wsi, const char * const *exec_array,
int script_uri_path_len, int timeout_secs,
const struct lws_protocol_vhost_options *mp_cgienv);
/**
* lws_cgi_write_split_stdout_headers: write cgi output accounting for header part
*
* \param wsi: connection to own the process
*/
LWS_VISIBLE LWS_EXTERN int
lws_cgi_write_split_stdout_headers(struct lws *wsi);
/**
* lws_cgi_kill: terminate cgi process associated with wsi
*
* \param wsi: connection to own the process
*/
LWS_VISIBLE LWS_EXTERN int
lws_cgi_kill(struct lws *wsi);
/**
* lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr
*
* \param wsi: parent wsi that has cgi
* \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch);
#endif
///@}

View File

@ -0,0 +1,457 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup client Client related functions
* ##Client releated functions
* \ingroup lwsapi
*
* */
///@{
/** enum lws_client_connect_ssl_connection_flags - flags that may be used
* with struct lws_client_connect_info ssl_connection member to control if
* and how SSL checks apply to the client connection being created
*/
enum lws_client_connect_ssl_connection_flags {
LCCSCF_USE_SSL = (1 << 0),
LCCSCF_ALLOW_SELFSIGNED = (1 << 1),
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2),
LCCSCF_ALLOW_EXPIRED = (1 << 3),
LCCSCF_ALLOW_INSECURE = (1 << 4),
LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM = (1 << 5),
LCCSCF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 6),
LCCSCF_H2_AUTH_BEARER = (1 << 7),
LCCSCF_H2_HEXIFY_AUTH_TOKEN = (1 << 8),
LCCSCF_H2_MANUAL_RXFLOW = (1 << 9),
LCCSCF_HTTP_MULTIPART_MIME = (1 << 10),
LCCSCF_HTTP_X_WWW_FORM_URLENCODED = (1 << 11),
LCCSCF_HTTP_NO_FOLLOW_REDIRECT = (1 << 12),
LCCSCF_HTTP_NO_CACHE_CONTROL = (1 << 13),
LCCSCF_ALLOW_REUSE_ADDR = (1 << 14),
/**< allow reuse local addresses in a bind call
* When the listening socket is bound to INADDR_ANY with a specific port
* then it is not possible to bind to this port for any local address
*/
LCCSCF_IPV6_PREFER_PUBLIC_ADDR = (1 << 15),
/**< RFC5014 - For IPv6 systems with SLAAC config, allow for preference
* to bind a socket to public address vs temporary private address
*/
LCCSCF_PIPELINE = (1 << 16),
/**< Serialize / pipeline multiple client connections
* on a single connection where possible.
*
* HTTP/1.0: possible if Keep-Alive: yes sent by server
* HTTP/1.1: always possible... uses pipelining
* HTTP/2: always possible... uses parallel streams
*/
LCCSCF_MUXABLE_STREAM = (1 << 17),
LCCSCF_H2_PRIOR_KNOWLEDGE = (1 << 18),
LCCSCF_WAKE_SUSPEND__VALIDITY = (1 << 19),
/* our validity checks are important enough to wake from suspend */
LCCSCF_PRIORITIZE_READS = (1 << 20),
/**<
* Normally lws balances reads and writes on all connections, so both
* are possible even on busy connections, and we go around the event
* loop more often to facilitate that, even if there is pending data.
*
* This flag indicates that you want to handle any pending reads on this
* connection without yielding the service loop for anything else. This
* means you may block other connection processing in favour of incoming
* data processing on this one if it receives back to back incoming rx.
*/
LCCSCF_SECSTREAM_CLIENT = (1 << 21),
/**< used to mark client wsi as bound to secure stream */
LCCSCF_SECSTREAM_PROXY_LINK = (1 << 22),
/**< client is a link between SS client and SS proxy */
LCCSCF_SECSTREAM_PROXY_ONWARD = (1 << 23),
/**< client the SS proxy's onward connection */
LCCSCF_IP_LOW_LATENCY = (1 << 24),
/**< set the "low delay" bit on the IP packets of this connection */
LCCSCF_IP_HIGH_THROUGHPUT = (1 << 25),
/**< set the "high throughput" bit on the IP packets of this
* connection */
LCCSCF_IP_HIGH_RELIABILITY = (1 << 26),
/**< set the "high reliability" bit on the IP packets of this
* connection */
LCCSCF_IP_LOW_COST = (1 << 27),
/**< set the "minimize monetary cost" bit on the IP packets of this
* connection */
LCCSCF_CONMON = (1 << 28),
/**< If LWS_WITH_CONMON enabled for build, keeps a copy of the
* getaddrinfo results so they can be queried subsequently */
LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS = (1 << 29),
/**< By default lws rejects https redirecting to http. Set this
* flag on the client connection to allow it. */
LCCSCF_CACHE_COOKIES = (1 << 30),
/**< If built with -DLWS_WITH_CACHE_NSCOOKIEJAR, store and reapply
* http cookies in a Netscape Cookie Jar on this connection */
};
/** struct lws_client_connect_info - parameters to connect with when using
* lws_client_connect_via_info() */
struct lws_client_connect_info {
struct lws_context *context;
/**< lws context to create connection in */
const char *address;
/**< remote address to connect to */
int port;
/**< remote port to connect to */
int ssl_connection;
/**< 0, or a combination of LCCSCF_ flags */
const char *path;
/**< URI path. Prefix with + for a UNIX socket. (+@ for
* a Linux abstract-namespace socket) */
const char *host;
/**< content of host header */
const char *origin;
/**< content of origin header */
const char *protocol;
/**< list of ws protocols we could accept */
int ietf_version_or_minus_one;
/**< deprecated: currently leave at 0 or -1 */
void *userdata;
/**< if non-NULL, use this as wsi user_data instead of malloc it */
const void *client_exts;
/**< UNUSED... provide in info.extensions at context creation time */
const char *method;
/**< if non-NULL, do this http method instead of ws[s] upgrade.
* use "GET" to be a simple http client connection. "RAW" gets
* you a connected socket that lws itself will leave alone once
* connected. */
struct lws *parent_wsi;
/**< if another wsi is responsible for this connection, give it here.
* this is used to make sure if the parent closes so do any
* child connections first. */
const char *uri_replace_from;
/**< if non-NULL, when this string is found in URIs in
* text/html content-encoding, it's replaced with uri_replace_to */
const char *uri_replace_to;
/**< see uri_replace_from */
struct lws_vhost *vhost;
/**< vhost to bind to (used to determine related SSL_CTX) */
struct lws **pwsi;
/**< if not NULL, store the new wsi here early in the connection
* process. Although we return the new wsi, the call to create the
* client connection does progress the connection somewhat and may
* meet an error that will result in the connection being scrubbed and
* NULL returned. While the wsi exists though, he may process a
* callback like CLIENT_CONNECTION_ERROR with his wsi: this gives the
* user callback a way to identify which wsi it is that faced the error
* even before the new wsi is returned and even if ultimately no wsi
* is returned.
*/
const char *iface;
/**< NULL to allow routing on any interface, or interface name or IP
* to bind the socket to */
int local_port;
/**< 0 to pick an ephemeral port, or a specific local port
* to bind the socket to */
const char *local_protocol_name;
/**< NULL: .protocol is used both to select the local protocol handler
* to bind to and as the list of remote ws protocols we could
* accept.
* non-NULL: this protocol name is used to bind the connection to
* the local protocol handler. .protocol is used for the
* list of remote ws protocols we could accept */
const char *alpn;
/**< NULL: allow lws default ALPN list, from vhost if present or from
* list of roles built into lws
* non-NULL: require one from provided comma-separated list of alpn
* tokens
*/
void *opaque_user_data;
/**< This data has no meaning to lws but is applied to the client wsi
* and can be retrieved by user code with lws_get_opaque_user_data().
* It's also provided with sequencer messages if the wsi is bound to
* an lws_seq_t.
*/
const lws_retry_bo_t *retry_and_idle_policy;
/**< optional retry and idle policy to apply to this connection.
* Currently only the idle parts are applied to the connection.
*/
int manual_initial_tx_credit;
/**< if LCCSCF_H2_MANUAL_REFLOW is set, this becomes the initial tx
* credit for the stream.
*/
uint8_t sys_tls_client_cert;
/**< 0 means no client cert. 1+ means apply lws_system client cert 0+
* to the client connection.
*/
uint8_t priority;
/**< 0 means normal priority... otherwise sets the IP priority on
* packets coming from this connection, from 1 - 7. Setting 7
* (network management priority) requires CAP_NET_ADMIN capability but
* the others can be set by anyone.
*/
#if defined(LWS_ROLE_MQTT)
const lws_mqtt_client_connect_param_t *mqtt_cp;
#else
void *mqtt_cp;
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
lws_fi_ctx_t fic;
/**< Attach external Fault Injection context to the client wsi,
* hierarchy is wsi -> vhost -> context */
#endif
/* for convenience, available when FI disabled in build */
const char *fi_wsi_name;
/**< specific Fault Injection namespace name for wsi created for this
* connection, allows targeting by "wsi=XXX/..." if you give XXX here.
*/
uint16_t keep_warm_secs;
/**< 0 means 5s. If the client connection to the endpoint becomes idle,
* defer closing it for this many seconds in case another outgoing
* connection to the same endpoint turns up.
*/
lws_log_cx_t *log_cx;
/**< NULL to use lws_context log context, else a pointer to a log
* context template to take a copy of for this wsi. Used to isolate
* wsi-specific logs into their own stream or file.
*/
const char *auth_username;
const char *auth_password;
#if defined(LWS_ROLE_WS)
uint8_t allow_reserved_bits;
/**< non-zero to allow reserved bits. You can get it by lws_get_reserved_bits().
* Note: default zero means close the websocket connection for non-zero rsv.
*/
uint8_t allow_unknown_opcode;
/**< non-zero to allow unknown opcode. You can get it by `lws_get_opcode`.
* None: default zero means close the websocket connection for unknown opcode.
*/
#endif
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility
*
* The below is to ensure later library versions with new
* members added above will see 0 (default) even if the app
* was not built against the newer headers.
*/
void *_unused[4]; /**< dummy */
};
/**
* lws_client_connect_via_info() - Connect to another websocket server
* \param ccinfo: pointer to lws_client_connect_info struct
*
* This function creates a connection to a remote server using the
* information provided in ccinfo.
*/
LWS_VISIBLE LWS_EXTERN struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info *ccinfo);
/**
* lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost
*
* \param info: client ssl related info
* \param vhost: which vhost to initialize client ssl operations on
*
* You only need to call this if you plan on using SSL client connections on
* the vhost. For non-SSL client connections, it's not necessary to call this.
*
* The following members of info are used during the call
*
* - options must have LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT set,
* otherwise the call does nothing
* - provided_client_ssl_ctx must be NULL to get a generated client
* ssl context, otherwise you can pass a prepared one in by setting it
* - ssl_cipher_list may be NULL or set to the client valid cipher list
* - ssl_ca_filepath may be NULL or client cert filepath
* - ssl_cert_filepath may be NULL or client cert filepath
* - ssl_private_key_filepath may be NULL or client cert private key
*
* You must create your vhost explicitly if you want to use this, so you have
* a pointer to the vhost. Create the context first with the option flag
* LWS_SERVER_OPTION_EXPLICIT_VHOSTS and then call lws_create_vhost() with
* the same info struct.
*/
LWS_VISIBLE LWS_EXTERN int
lws_init_vhost_client_ssl(const struct lws_context_creation_info *info,
struct lws_vhost *vhost);
/**
* lws_http_client_read() - consume waiting received http client data
*
* \param wsi: client connection
* \param buf: pointer to buffer pointer - fill with pointer to your buffer
* \param len: pointer to chunk length - fill with max length of buffer
*
* This is called when the user code is notified client http data has arrived.
* The user code may choose to delay calling it to consume the data, for example
* waiting until an onward connection is writeable.
*
* For non-chunked connections, up to len bytes of buf are filled with the
* received content. len is set to the actual amount filled before return.
*
* For chunked connections, the linear buffer content contains the chunking
* headers and it cannot be passed in one lump. Instead, this function will
* call back LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ with in pointing to the
* chunk start and len set to the chunk length. There will be as many calls
* as there are chunks or partial chunks in the buffer.
*/
LWS_VISIBLE LWS_EXTERN int
lws_http_client_read(struct lws *wsi, char **buf, int *len);
/**
* lws_http_client_http_response() - get last HTTP response code
*
* \param wsi: client connection
*
* Returns the last server response code, eg, 200 for client http connections.
* If there is no valid response, it will return 0.
*
* You should capture this during the LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP
* callback, because after that the memory reserved for storing the related
* headers is freed and this value is lost.
*/
LWS_VISIBLE LWS_EXTERN unsigned int
lws_http_client_http_response(struct lws *wsi);
/**
* lws_tls_client_vhost_extra_cert_mem() - add more certs to vh client tls ctx
*
* \param vh: the vhost to give more client certs to
* \param der: pointer to der format additional cert
* \param der_len: size in bytes of der
*
* After the vhost is created with one cert for client verification, you
* can add additional, eg, intermediate, certs to the client tls context
* of the vhost, for use with validating the incoming server cert(s).
*/
LWS_VISIBLE LWS_EXTERN int
lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh,
const uint8_t *der, size_t der_len);
/**
* lws_client_http_body_pending() - control if client connection needs to send body
*
* \param wsi: client connection
* \param something_left_to_send: nonzero if need to send more body, 0 (default)
* if nothing more to send
*
* If you will send payload data with your HTTP client connection, eg, for POST,
* when you set the related http headers in
* LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER callback you should also call
* this API with something_left_to_send nonzero, and call
* lws_callback_on_writable(wsi);
*
* After sending the headers, lws will call your callback with
* LWS_CALLBACK_CLIENT_HTTP_WRITEABLE reason when writable. You can send the
* next part of the http body payload, calling lws_callback_on_writable(wsi);
* if there is more to come, or lws_client_http_body_pending(wsi, 0); to
* let lws know the last part is sent and the connection can move on.
*/
LWS_VISIBLE LWS_EXTERN void
lws_client_http_body_pending(struct lws *wsi, int something_left_to_send);
/**
* lws_client_http_multipart() - issue appropriate multipart header or trailer
*
* \param wsi: client connection
* \param name: multipart header name field, or NULL if end of multipart
* \param filename: multipart header filename field, or NULL if none
* \param content_type: multipart header content-type part, or NULL if none
* \param p: pointer to position in buffer
* \param end: end of buffer
*
* This issues a multipart mime boundary, or terminator if name = NULL.
*
* Returns 0 if OK or nonzero if couldn't fit in buffer
*/
LWS_VISIBLE LWS_EXTERN int
lws_client_http_multipart(struct lws *wsi, const char *name,
const char *filename, const char *content_type,
char **p, char *end);
/**
* lws_http_basic_auth_gen() - helper to encode client basic auth string
*
* \param user: user name
* \param pw: password
* \param buf: where to store base64 result
* \param len: max usable size of buf
*
* Encodes a username and password in Basic Auth format for use with the
* Authorization header. On return, buf is filled with something like
* "Basic QWxhZGRpbjpPcGVuU2VzYW1l".
*/
LWS_VISIBLE LWS_EXTERN int
lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len);
/**
* lws_http_basic_auth_gen2() - helper to encode client basic auth string
*
* \param user: user name
* \param pw: password
* \param pwd_len: count of bytes in password
* \param buf: where to store base64 result
* \param len: max usable size of buf
*
* Encodes a username and password in Basic Auth format for use with the
* Authorization header. On return, buf is filled with something like
* "Basic QWxhZGRpbjpPcGVuU2VzYW1l".
*
* This differs from lws_http_baic_auth_gen() in that NUL bytes can
* appear in the password due to an explicit password length argument.
*/
LWS_VISIBLE LWS_EXTERN int
lws_http_basic_auth_gen2(const char *user, const void *pw, size_t pwd_len,
char *buf, size_t len);
/**
* lws_tls_session_is_reused() - returns nonzero if tls session was cached
*
* \param wsi: the wsi
*
* Returns zero if the tls session is fresh, else nonzero if the tls session was
* taken from the cache. If lws is built with LWS_WITH_TLS_SESSIONS and the vhost
* was created with the option LWS_SERVER_OPTION_ENABLE_TLS_SESSION_CACHE, then
* on full tls session establishment of a client connection, the session is added
* to the tls cache.
*
* This lets you find out if your session was new (0) or from the cache (nonzero),
* it'a mainly useful for stats and testing.
*/
LWS_VISIBLE LWS_EXTERN int
lws_tls_session_is_reused(struct lws *wsi);
///@}

View File

@ -0,0 +1,155 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup conmon Connection Latency information
* ## Connection Latency information
*
* When LWS_WITH_CONMON is enabled at build, collects detailed statistics
* about the client connection setup latency, available to the connection
* itself
*/
///@{
/* enough for 4191s, or just over an hour */
typedef uint32_t lws_conmon_interval_us_t;
/*
* Connection latency information... note that not all wsi actually make
* connections, for example h2 streams after the initial one will have 0
* for everything except ciu_txn_resp.
*
* If represented in JSON, it should look like this
*
* {
* "peer": "46.105.127.147",
* "dns_us": 1234,
* "dns_disp": 1,
* "sockconn_us": 1234,
* "tls_us": 1234,
* "txn_resp_us": 1234,
* "dns":["46.105.127.147", "2001:41d0:2:ee93::1"],
* "prot_specific": {
* "protocol": "http",
* "resp": 200
* }
* }
*
* The indexes in "dns_disp" are declared in lws_conmon_dns_disposition_t
* below.
*
* "prot_specific" may not be present if the protocol doesn't have anything
* to report or is not supported.
*/
typedef enum lws_conmon_pcol {
LWSCONMON_PCOL_NONE,
LWSCONMON_PCOL_HTTP, /* .protocol_specific.http is valid */
} lws_conmon_pcol_t;
typedef enum lws_conmon_dns_disposition {
LWSCONMON_DNS_NONE,
/**< did not attempt DNS */
LWSCONMON_DNS_OK = 1,
/**< DNS lookup did give results */
LWSCONMON_DNS_SERVER_UNREACHABLE = 2,
/**< DNS server was not reachable */
LWSCONMON_DNS_NO_RESULT = 3
/**< DNS server replied but nothing usable */
} lws_conmon_dns_disposition_t;
struct lws_conmon {
lws_sockaddr46 peer46;
/**< The peer we actually connected to, if any. .peer46.sa4.sa_family
* is either 0 if invalid, or the AF_ */
union {
struct {
int response;
/**< h1 http response code */
} http;
} protocol_specific;
/**< possibly-present protocol-specific additional information. This
* is only valid for the first transaction after connection and does
* not capture results for persistent or muxed connections like ws
* messages, mqtt messages, or h2 streams */
struct addrinfo *dns_results_copy;
/**< NULL, or Allocated copy of dns results, owned by this object and
* freed when object destroyed.
* Only set if client flag LCCSCF_CONMON applied */
lws_conmon_interval_us_t ciu_dns;
/**< 0, or if a socket connection, us taken to acquire this DNS response
*
*/
lws_conmon_interval_us_t ciu_sockconn;
/**< 0, or if connection-based, the us interval between the socket
* connect() attempt that succeeded, and the connection setup */
lws_conmon_interval_us_t ciu_tls;
/**< 0 if no tls, or us taken to establish the tls tunnel */
lws_conmon_interval_us_t ciu_txn_resp;
/**< 0, or if the protocol supports transactions, the interval between
* sending the initial transaction request and starting to receive the
* response */
lws_conmon_pcol_t pcol;
/**< indicates which extra protocol_specific info member is valid,
* if any */
lws_conmon_dns_disposition_t dns_disposition;
/**< indicates general disposition of DNS request */
};
/**
* lws_conmon_wsi_take() - create a connection latency object from client wsi
*
* \param context: lws wsi
* \param dest: conmon struct to fill
*
* Copies wsi conmon data into the caller's struct. Passes ownership of
* any allocations in the addrinfo list to the caller, lws will not delete that
* any more on wsi close after this call. The caller must call
* lws_conmon_release() on the struct to destroy any addrinfo in the struct
* that is prepared by this eventually but it can defer it as long as it wants.
*
* Other than the addrinfo list, the contents of the returned object are
* completely selfcontained and don't point outside of the object itself, ie,
* everything else in there remains in scope while the object itself does.
*/
LWS_VISIBLE LWS_EXTERN void
lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest);
/**
* lws_conmon_release() - free any allocations in the conmon struct
*
* \param conmon: pointer to conmon struct
*
* Destroys any allocations in the conmon struct so it can go out of scope.
* It doesn't free \p dest itself, it's designed to clean out a struct that
* is on the stack or embedded in another object.
*/
LWS_VISIBLE LWS_EXTERN void
lws_conmon_release(struct lws_conmon *conmon);
///@}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,511 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup cose COSE apis
* ##COSE related functions
* \ingroup lwsaoi
*
* COSE RFC 8152 relates to signed and encrypted CBOR
*/
//@{
enum {
/* RFC8152: Table 2: Common Header Parameters
* https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
*/
LWSCOSE_WKL_ALG = 1, /* int / tstr */
LWSCOSE_WKL_CRIT, /* [+ label ] */
LWSCOSE_WKL_CONTENT_TYPE, /* tstr / uint */
LWSCOSE_WKL_KID, /* bstr */
LWSCOSE_WKL_IV, /* bstr */
LWSCOSE_WKL_IV_PARTIAL, /* bstr */
LWSCOSE_WKL_COUNTERSIG, /* COSE sig(s) */
LWSCOSE_WKL_COUNTERSIG0 = 9, /* bstr */
LWSCOSE_WKL_KID_CONTEXT, /* bstr */
LWSCOSE_WKL_CUPH_NONCE = 256, /* bstr */
LWSCOSE_WKL_CUPH_OWNER_PUBKEY = 257, /* array */
/* RFC8152: Table 3: key map labels */
LWSCOSE_WKK_KTY = 1, /* int / tstr */
LWSCOSE_WKK_KID, /* bstr */
LWSCOSE_WKK_ALG, /* int / tstr */
LWSCOSE_WKK_KEY_OPS, /* [ + (int / tstr) ] */
LWSCOSE_WKK_BASE_IV, /* bstr */
/* RFC8152: Table 4: Key Operation Values */
LWSCOSE_WKKO_SIGN = 1,
LWSCOSE_WKKO_VERIFY,
LWSCOSE_WKKO_ENCRYPT,
LWSCOSE_WKKO_DECRYPT,
LWSCOSE_WKKO_WRAP_KEY,
LWSCOSE_WKKO_UNWRAP_KEY,
LWSCOSE_WKKO_DERIVE_KEY,
LWSCOSE_WKKO_DERIVE_BITS,
LWSCOSE_WKKO_MAC_CREATE,
LWSCOSE_WKKO_MAC_VERIFY,
/* RFC8152: Table 5: ECDSA algs */
LWSCOSE_WKAECDSA_ALG_ES256 = -7,
LWSCOSE_WKAECDSA_ALG_ES384 = -35,
LWSCOSE_WKAECDSA_ALG_ES512 = -36,
/* RFC8152: Table 6: EDDSA algs */
LWSCOSE_WKAEDDSA_ALG_EDDSA = -8,
/* RFC8152: Table 7: HMAC algs */
LWSCOSE_WKAHMAC_256_64 = 4,
LWSCOSE_WKAHMAC_256_256,
LWSCOSE_WKAHMAC_384_384,
LWSCOSE_WKAHMAC_512_512,
/* RFC8152: Table 8: AES algs */
LWSCOSE_WKAAES_128_64 = 14,
LWSCOSE_WKAAES_256_64,
LWSCOSE_WKAAES_128_128 = 25,
LWSCOSE_WKAAES_256_128,
/* RFC8152: Table 9: AES GCM algs */
LWSCOSE_WKAAESGCM_128 = 1,
LWSCOSE_WKAAESGCM_192,
LWSCOSE_WKAAESGCM_256,
/* RFC8152: Table 10: AES CCM algs */
LWSCOSE_WKAAESCCM_16_64_128 = 10,
LWSCOSE_WKAAESCCM_16_64_256,
LWSCOSE_WKAAESCCM_64_64_128,
LWSCOSE_WKAAESCCM_64_64_256,
LWSCOSE_WKAAESCCM_16_128_128,
LWSCOSE_WKAAESCCM_16_128_256,
LWSCOSE_WKAAESCCM_64_128_128,
LWSCOSE_WKAAESCCM_64_128_256,
/* RFC8152: Table 11: CHACHA20 / Poly1305 */
LWSCOSE_WKACHACHA_POLY1305 = 24,
/* RFC8152: Table 13: HKDF param */
LWSCOSE_WKAPHKDF_SALT = -20,
/* RFC8152: Table 14: Context Algorithm Parameters */
LWSCOSE_WKAPCTX_PARTY_U_IDENTITY = -21,
LWSCOSE_WKAPCTX_PARTY_U_NONCE = -22,
LWSCOSE_WKAPCTX_PARTY_U_OTHER = -23,
LWSCOSE_WKAPCTX_PARTY_V_IDENTITY = -24,
LWSCOSE_WKAPCTX_PARTY_V_NONCE = -25,
LWSCOSE_WKAPCTX_PARTY_V_OTHER = -26,
/* RFC8152: Table 15: Direct key */
LWSCOSE_WKK_DIRECT_CEK = -6,
/* RFC8152: Table 16: Direct key with KDF */
LWSCOSE_WKK_DIRECT_HKDF_SHA_256 = -10,
LWSCOSE_WKK_DIRECT_HKDF_SHA_512 = -11,
LWSCOSE_WKK_DIRECT_HKDF_AES_128 = -12,
LWSCOSE_WKK_DIRECT_HKDF_AES_256 = -13,
/* RFC8152: Table 17: AES Key Wrap Algorithm Values */
LWSCOSE_WKK_DIRECT_HKDFKW_SHA_256 = -3,
LWSCOSE_WKK_DIRECT_HKDFKW_SHA_512 = -4,
LWSCOSE_WKK_DIRECT_HKDFKW_AES_128 = -5,
/* RFC8152: Table 18: ECDH Algorithm Values */
LWSCOSE_WKAECDH_ALG_ES_HKDF_256 = -25,
LWSCOSE_WKAECDH_ALG_ES_HKDF_512 = -26,
LWSCOSE_WKAECDH_ALG_SS_HKDF_256 = -27,
LWSCOSE_WKAECDH_ALG_SS_HKDF_512 = -28,
/* RFC8152: Table 19: ECDH Algorithm Parameters */
LWSCOSE_WKAPECDH_EPHEMERAL_KEY = -1,
LWSCOSE_WKAPECDH_STATIC_KEY = -2,
LWSCOSE_WKAPECDH_STATIC_KEY_ID = -3,
/* RFC8152: Table 20: ECDH Algorithm Parameters with key wrap */
LWSCOSE_WKAPECDH_ES_A128KW = -29,
LWSCOSE_WKAPECDH_ES_A192KW = -30,
LWSCOSE_WKAPECDH_ES_A256KW = -31,
LWSCOSE_WKAPECDH_SS_A128KW = -32,
LWSCOSE_WKAPECDH_SS_A192KW = -33,
LWSCOSE_WKAPECDH_SS_A256KW = -34,
/* RFC8152: Table 21: Key Type Values
* https://www.iana.org/assignments/cose/cose.xhtml#key-type
*/
LWSCOSE_WKKTV_OKP = 1,
LWSCOSE_WKKTV_EC2 = 2,
LWSCOSE_WKKTV_RSA = 3,
LWSCOSE_WKKTV_SYMMETRIC = 4,
LWSCOSE_WKKTV_HSS_LMS = 5,
LWSCOSE_WKKTV_WALNUTDSA = 6,
/* RFC8152: Table 22: Elliptic Curves
* https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
*/
LWSCOSE_WKEC_P256 = 1,
LWSCOSE_WKEC_P384,
LWSCOSE_WKEC_P521,
LWSCOSE_WKEC_X25519,
LWSCOSE_WKEC_X448,
LWSCOSE_WKEC_ED25519,
LWSCOSE_WKEC_ED448,
LWSCOSE_WKEC_SECP256K1,
/* RFC8152: Table 23: EC Key Parameters */
LWSCOSE_WKECKP_CRV = -1,
LWSCOSE_WKECKP_X = -2,
LWSCOSE_WKECKP_Y = -3,
LWSCOSE_WKECKP_D = -4,
/* RFC8152: Table 24: Octet Key Pair (OKP) Parameters */
LWSCOSE_WKOKP_CRV = -1,
LWSCOSE_WKOKP_X = -2,
LWSCOSE_WKOKP_D = -4,
/* Additional from
* https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
*/
LWSCOSE_WKKPRSA_N = -1,
LWSCOSE_WKKPRSA_E = -2,
LWSCOSE_WKKPRSA_D = -3,
LWSCOSE_WKKPRSA_P = -4,
LWSCOSE_WKKPRSA_Q = -5,
LWSCOSE_WKKPRSA_DP = -6,
LWSCOSE_WKKPRSA_DQ = -7,
LWSCOSE_WKKPRSA_QINV = -8,
LWSCOSE_WKKPRSA_OTHER = -9,
LWSCOSE_WKKPRSA_RI = -10,
LWSCOSE_WKKPRSA_DI = -11,
LWSCOSE_WKKPRSA_TI = -12,
/* RFC8152: Table 25: Symmetric Key Parameters */
LWSCOSE_WKSYMKP_KEY_VALUE = 4,
/* RFC8152: Table 26: CoAP Content-Formats for COSE */
LWSCOAP_CONTENTFORMAT_COSE_SIGN = 98,
LWSCOAP_CONTENTFORMAT_COSE_SIGN1 = 18,
LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT = 96,
LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT0 = 16,
LWSCOAP_CONTENTFORMAT_COSE_MAC = 97,
LWSCOAP_CONTENTFORMAT_COSE_MAC0 = 17,
LWSCOAP_CONTENTFORMAT_COSE_KEY = 101,
LWSCOAP_CONTENTFORMAT_COSE_KEY_SET = 102,
/* RFC8152: Table 27: Header Parameter for CounterSignature0 */
LWSCOSE_WKL_COUNTERSIGNATURE0 = 9, /* bstr */
/* RFC8812: Table 1: RSASSA-PKCS1-v1_5 Algorithm Values */
LWSCOSE_WKARSA_ALG_RS256 = -257, /* + SHA-256 */
LWSCOSE_WKARSA_ALG_RS384 = -258, /* + SHA-384 */
LWSCOSE_WKARSA_ALG_RS512 = -259, /* + SHA-512 */
};
enum enum_cose_key_meta_tok {
COSEKEY_META_KTY,
COSEKEY_META_KID,
COSEKEY_META_KEY_OPS,
COSEKEY_META_BASE_IV,
COSEKEY_META_ALG,
LWS_COUNT_COSE_KEY_ELEMENTS
};
typedef int64_t cose_param_t;
LWS_VISIBLE LWS_EXTERN const char *
lws_cose_alg_to_name(cose_param_t alg);
LWS_VISIBLE LWS_EXTERN cose_param_t
lws_cose_name_to_alg(const char *name);
/*
* cose_key
*/
typedef struct lws_cose_key {
/* key data elements */
struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT];
/* generic meta key elements, like KID */
struct lws_gencrypto_keyelem meta[LWS_COUNT_COSE_KEY_ELEMENTS];
lws_dll2_t list; /* used when part of a set */
int gencrypto_kty; /**< one of LWS_GENCRYPTO_KTY_ */
cose_param_t kty;
cose_param_t cose_alg;
cose_param_t cose_curve;
char private_key; /* nonzero = has private key elements */
} lws_cose_key_t;
typedef int (*lws_cose_key_import_callback)(struct lws_cose_key *s, void *user);
/** lws_cose_jwk_import() - Create an lws_cose_key_t object from cose_key CBOR
*
* \param pkey_set: NULL, or a pointer to an lws_dll2_owner_t for a cose_key set
* \param cb: callback for each jwk-processed key, or NULL if importing a single
* key with no parent "keys" JSON
* \param user: pointer to be passed to the callback, otherwise ignored by lws.
* NULL if importing a single key with no parent "keys" JSON
* \param in: a single cose_key
* \param len: the length of the cose_key in bytes
*
* Creates a single lws_cose_key_t if \p pkey_set is NULL or if the incoming
* CBOR doesn't start with an array, otherwise expects a CBOR array containing
* zero or more cose_key CBOR, and adds each to the \p pkey_set
* lws_dll2_owner_t struct. Created lws_cose_key_t are filled with data from
* the COSE representation and can be used with other COSE crypto ops.
*/
LWS_VISIBLE LWS_EXTERN lws_cose_key_t *
lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb,
void *user, const uint8_t *in, size_t len);
/** lws_cose_key_export() - Create cose_key CBOR from an lws_cose_key_t
*
* \param ck: the lws_cose_key_t to export to CBOR
* \param ctx: the CBOR writing context (same as for lws_lec_printf())
* \param flags: 0 to export only public elements, or LWSJWKF_EXPORT_PRIVATE
*
* Creates an lws_jwk struct filled with data from the COSE representation.
*/
LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret
lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags);
/**
* lws_cose_key_generate() - generate a fresh key
*
* \param context: the lws_context used to get random
* \param cose_kty: one of LWSCOSE_WKKTV_ indicating the well-known key type
* \param use_mask: 0, or a bitfield where (1 << LWSCOSE_WKKO_...) set means valid for use
* \param bits: key bits for RSA
* \param curve: for EC keys, one of "P-256", "P-384" or "P-521" currently
* \param kid: string describing the key, or NULL
*
* Create an lws_cose_key_t of the specified type and return it
*/
LWS_VISIBLE LWS_EXTERN lws_cose_key_t *
lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty,
int use_mask, int bits, const char *curve,
const uint8_t *kid, size_t kl);
LWS_VISIBLE LWS_EXTERN lws_cose_key_t *
lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl);
LWS_VISIBLE LWS_EXTERN void
lws_cose_key_destroy(lws_cose_key_t **ck);
LWS_VISIBLE LWS_EXTERN void
lws_cose_key_set_destroy(lws_dll2_owner_t *o);
/* only available in _DEBUG build */
LWS_VISIBLE LWS_EXTERN void
lws_cose_key_dump(const lws_cose_key_t *ck);
/*
* cose_sign
*/
struct lws_cose_validate_context;
enum lws_cose_sig_types {
SIGTYPE_UNKNOWN,
SIGTYPE_MULTI,
SIGTYPE_SINGLE,
SIGTYPE_COUNTERSIGNED, /* not yet supported */
SIGTYPE_MAC, /* only supported for validation */
SIGTYPE_MAC0,
};
/* a list of these result objects is the output of the validation process */
typedef struct {
lws_dll2_t list;
const lws_cose_key_t *cose_key;
cose_param_t cose_alg;
int result; /* 0 = validated */
} lws_cose_validate_res_t;
enum {
LCOSESIGEXTCB_RET_FINISHED,
LCOSESIGEXTCB_RET_AGAIN,
LCOSESIGEXTCB_RET_ERROR = -1
};
typedef struct {
struct lws_cose_validate_context *cps;
const uint8_t *ext;
size_t xl;
} lws_cose_sig_ext_pay_t;
typedef int (*lws_cose_sign_ext_pay_cb_t)(lws_cose_sig_ext_pay_t *x);
typedef int (*lws_cose_validate_pay_cb_t)(struct lws_cose_validate_context *cps,
void *opaque, const uint8_t *paychunk,
size_t paychunk_len);
typedef struct lws_cose_validate_create_info {
struct lws_context *cx;
/**< REQUIRED: the lws context */
lws_dll2_owner_t *keyset;
/**< REQUIRED: one or more cose_keys */
enum lws_cose_sig_types sigtype;
/**< 0 if a CBOR tag is in the sig, else one of SIGTYPE_MULTI,
* SIGTYPE_SINGLE, etc*/
lws_cose_validate_pay_cb_t pay_cb;
/**< optional: called back with unvalidated payload pieces */
void *pay_opaque;
/**< optional: passed into pay_cb callback along with payload chunk */
lws_cose_sign_ext_pay_cb_t ext_cb;
/**< optional extra application data provision callback */
void *ext_opaque;
/**< optional extra application data provision callback opaque */
size_t ext_len;
/**< if we have extra app data, this must be set to the length of it */
} lws_cose_validate_create_info_t;
/**
* lws_cose_validate_create() - create a signature validation context
*
* \param info: struct describing the validation context to create
*
* Creates a signature validation context set up as described in \p info.
*
* You can then pass the signature cbor chunks to it using
* lws_cose_validate_chunk(), finialize and get the results list using
* lws_cose_validate_results() and destroy with lws_cose_validate_destroy().
*/
LWS_VISIBLE LWS_EXTERN struct lws_cose_validate_context *
lws_cose_validate_create(const lws_cose_validate_create_info_t *info);
/**
* lws_cose_validate_chunk() - passes chunks of CBOR into the signature validator
*
* \param cps: the validation context
* \param in: the chunk of CBOR (does not have to be logically complete)
* \param in_len: number of bytes available at \p in
*
* Parses signature CBOR to produce a list of result objects.
*
*
*/
LWS_VISIBLE LWS_EXTERN int
lws_cose_validate_chunk(struct lws_cose_validate_context *cps,
const uint8_t *in, size_t in_len, size_t *used_in);
LWS_VISIBLE LWS_EXTERN lws_dll2_owner_t *
lws_cose_validate_results(struct lws_cose_validate_context *cps);
LWS_VISIBLE LWS_EXTERN void
lws_cose_validate_destroy(struct lws_cose_validate_context **cps);
struct lws_cose_sign_context;
#define LCSC_FL_ADD_CBOR_TAG (1 << 0)
#define LCSC_FL_ADD_CBOR_PREFER_MAC0 (1 << 1)
typedef struct lws_cose_sign_create_info {
struct lws_context *cx;
/**< REQUIRED: the lws context */
lws_dll2_owner_t *keyset;
/**< REQUIRED: one or more cose_keys */
lws_lec_pctx_t *lec;
/**< REQUIRED: the cbor output context to emit to, user must
* initialize with lws_lec_init() beforehand */
lws_cose_sign_ext_pay_cb_t ext_cb;
/**< optional extra application data provision callback */
void *ext_opaque;
/**< optional extra application data provision callback opaque */
size_t ext_len;
/**< if we have extra app data, this must be set to the length of it */
size_t inline_payload_len;
/**< REQUIRED: size of the inline payload we will provide */
int flags;
/**< bitmap of LCSC_FL_* */
enum lws_cose_sig_types sigtype;
/**< 0, or sign type hint */
} lws_cose_sign_create_info_t;
/**
* lws_cose_sign_create() - Create a signing context
*
* \param info: a structure describing the signing context you want to create
*
* This allocates and returns a signing context created according to what is in
* the \p info parameter.
*
* \p info must be prepared with the lws_context, a keyset to use, a CBOR
* output context, and the inline payload length.
*
* Returns NULL on failure or the created signing context ready to add alg(s)
* to.
*/
LWS_VISIBLE LWS_EXTERN struct lws_cose_sign_context *
lws_cose_sign_create(const lws_cose_sign_create_info_t *info);
LWS_VISIBLE LWS_EXTERN int
lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg,
const lws_cose_key_t *ck);
LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret
lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc,
const uint8_t *in, size_t in_len);
LWS_VISIBLE LWS_EXTERN void
lws_cose_sign_destroy(struct lws_cose_sign_context **csc);
//@}

View File

@ -0,0 +1,94 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* must be included manually as
*
* #include <libwebsockets/lws-dbus.h>
*
* if dbus apis needed
*/
#if !defined(__LWS_DBUS_H__)
#define __LWS_DBUS_H__
#include <dbus/dbus.h>
/* helper type to simplify implementing methods as individual functions */
typedef DBusHandlerResult (*lws_dbus_message_handler)(DBusConnection *conn,
DBusMessage *message, DBusMessage **reply, void *d);
struct lws_dbus_ctx;
typedef void (*lws_dbus_closing_t)(struct lws_dbus_ctx *ctx);
struct lws_dbus_ctx {
struct lws_dll2_owner owner; /* dbusserver ctx: HEAD of accepted list */
struct lws_dll2 next; /* dbusserver ctx: HEAD of accepted list */
struct lws_vhost *vh; /* the vhost we logically bind to in lws */
int tsi; /* the lws thread service index (0 if only one service
thread as is the default */
DBusConnection *conn;
DBusServer *dbs;
DBusWatch *w[4];
DBusPendingCall *pc;
char hup;
char timeouts;
/* cb_closing callback will be called after the connection and this
* related ctx struct have effectively gone out of scope.
*
* The callback should close and clean up the connection and free the
* ctx.
*/
lws_dbus_closing_t cb_closing;
};
/**
* lws_dbus_connection_setup() - bind dbus connection object to lws event loop
*
* \param ctx: additional information about the connection
* \param conn: the DBusConnection object to bind
*
* This configures a DBusConnection object to use lws for watchers and timeout
* operations.
*/
LWS_VISIBLE LWS_EXTERN int
lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn,
lws_dbus_closing_t cb_closing);
/**
* lws_dbus_server_listen() - bind dbus connection object to lws event loop
*
* \param ctx: additional information about the connection
* \param ads: the DBUS address to listen on, eg, "unix:abstract=mysocket"
* \param err: a DBusError object to take any extra error information
* \param new_conn: a callback function to prepare new accepted connections
*
* This creates a DBusServer and binds it to the lws event loop, and your
* callback to accept new connections.
*/
LWS_VISIBLE LWS_EXTERN DBusServer *
lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads,
DBusError *err, DBusNewConnectionFunction new_conn);
#endif

View File

@ -0,0 +1,187 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup diskcache LWS disk cache
* ## Disk cache API
*
* Lws provides helper apis useful if you need a disk cache containing hashed
* files and need to delete files from it on an LRU basis to keep it below some
* size limit.
*
* The API `lws_diskcache_prepare()` deals with creating the cache dir and
* 256 subdirs, which are used according to the first two chars of the hex
* hash of the cache file.
*
* `lws_diskcache_create()` and `lws_diskcache_destroy()` allocate and free
* an opaque struct that represents the disk cache.
*
* `lws_diskcache_trim()` should be called at eg, 1s intervals to perform the
* cache dir monitoring and LRU autodelete in the background lazily. It can
* be done in its own thread or on a timer... it monitors the directories in a
* stateful way that stats one or more file in the cache per call, and keeps
* a list of the oldest files as it goes. When it completes a scan, if the
* aggregate size is over the limit, it will delete oldest files first to try
* to keep it under the limit.
*
* The cache size monitoring is extremely efficient in time and memory even when
* the cache directory becomes huge.
*
* `lws_diskcache_query()` is used to determine if the file already exists in
* the cache, or if it must be created. If it must be created, then the file
* is opened using a temp name that must be converted to a findable name with
* `lws_diskcache_finalize_name()` when the generation of the file contents are
* complete. Aborted cached files that did not complete generation will be
* flushed by the LRU eventually. If the file already exists, it is 'touched'
* to make it new again and the fd returned.
*
*/
///@{
struct lws_diskcache_scan;
/**
* lws_diskcache_create() - creates an opaque struct representing the disk cache
*
* \param cache_dir_base: The cache dir path, eg `/var/cache/mycache`
* \param cache_size_limit: maximum size on disk the cache is allowed to use
*
* This returns an opaque `struct lws_diskcache_scan *` which represents the
* disk cache, the trim scanning state and so on. You should use
* `lws_diskcache_destroy()` to free it to destroy it.
*/
LWS_VISIBLE LWS_EXTERN struct lws_diskcache_scan *
lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit);
/**
* lws_diskcache_destroy() - destroys the pointer returned by ...create()
*
* \param lds: pointer to the pointer returned by lws_diskcache_create()
*
* Frees *lds and any allocations it did, and then sets *lds to NULL and
* returns.
*/
LWS_VISIBLE LWS_EXTERN void
lws_diskcache_destroy(struct lws_diskcache_scan **lds);
/**
* lws_diskcache_prepare() - ensures the cache dir structure exists on disk
*
* \param cache_base_dir: The cache dir path, eg `/var/cache/mycache`
* \param mode: octal dir mode to enforce, like 0700
* \param uid: uid the cache dir should belong to
*
* This should be called while your app is still privileged. It will create
* the cache directory structure on disk as necessary, enforce the given access
* mode on it and set the given uid as the owner. It won't make any trouble
* if the cache already exists.
*
* Typically the mode is 0700 and the owner is the user that your application
* will transition to use when it drops root privileges.
*/
LWS_VISIBLE LWS_EXTERN int
lws_diskcache_prepare(const char *cache_base_dir, int mode, uid_t uid);
#define LWS_DISKCACHE_QUERY_NO_CACHE 0
#define LWS_DISKCACHE_QUERY_EXISTS 1
#define LWS_DISKCACHE_QUERY_CREATING 2
#define LWS_DISKCACHE_QUERY_ONGOING 3 /* something else is creating it */
/**
* lws_diskcache_query() - ensures the cache dir structure exists on disk
*
* \param lds: The opaque struct representing the disk cache
* \param is_bot: nonzero means the request is from a bot. Don't create new cache contents if so.
* \param hash_hex: hex string representation of the cache object hash
* \param _fd: pointer to the fd to be set
* \param cache: destination string to take the cache filepath
* \param cache_len: length of the buffer at `cache`
* \param extant_cache_len: pointer to a size_t to take any extant cached file size
*
* This function is called when you want to find if the hashed name already
* exists in the cache. The possibilities for the return value are
*
* - LWS_DISKCACHE_QUERY_NO_CACHE: It's not in the cache and you can't create
* it in the cache for whatever reason.
* - LWS_DISKCACHE_QUERY_EXISTS: It exists in the cache. It's open RDONLY and
* *_fd has been set to the file descriptor. *extant_cache_len has been set
* to the size of the cached file in bytes. cache has been set to the
* full filepath of the cached file. Closing _fd is your responsibility.
* - LWS_DISKCACHE_QUERY_CREATING: It didn't exist, but a temp file has been
* created in the cache and *_fd set to a file descriptor opened on it RDWR.
* You should create the contents, and call `lws_diskcache_finalize_name()`
* when it is done. Closing _fd is your responsibility.
* - LWS_DISKCACHE_QUERY_ONGOING: not returned by this api, but you may find it
* desirable to make a wrapper function which can handle another asynchronous
* process that is already creating the cached file. This can be used to
* indicate that situation externally... how to determine the same thing is
* already being generated is out of scope of this api.
*/
LWS_VISIBLE LWS_EXTERN int
lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot,
const char *hash_hex, int *_fd, char *cache, int cache_len,
size_t *extant_cache_len);
/**
* lws_diskcache_query() - ensures the cache dir structure exists on disk
*
* \param cache: The cache file temp name returned with LWS_DISKCACHE_QUERY_CREATING
*
* This renames the cache file you are creating to its final name. It should
* be called on the temp name returned by `lws_diskcache_query()` if it gave a
* LWS_DISKCACHE_QUERY_CREATING return, after you have filled the cache file and
* closed it.
*/
LWS_VISIBLE LWS_EXTERN int
lws_diskcache_finalize_name(char *cache);
/**
* lws_diskcache_trim() - performs one or more file checks in the cache for size management
*
* \param lds: The opaque object representing the cache
*
* This should be called periodically to statefully walk the cache on disk
* collecting the oldest files. When it has visited every file, if the cache
* is oversize it will delete the oldest files until it's back under size again.
*
* Each time it's called, it will look at one or more dir in the cache. If
* called when the cache is oversize, it increases the amount of work done each
* call until it is reduced again. Typically it will take 256 calls before it
* deletes anything, so if called once per second, it will delete files once
* every 4 minutes. Each call is very inexpensive both in memory and time.
*/
LWS_VISIBLE LWS_EXTERN int
lws_diskcache_trim(struct lws_diskcache_scan *lds);
/**
* lws_diskcache_secs_to_idle() - see how long to idle before calling trim
*
* \param lds: The opaque object representing the cache
*
* If the cache is undersize, there's no need to monitor it immediately. This
* suggests how long to "sleep" before calling `lws_diskcache_trim()` again.
*/
LWS_VISIBLE LWS_EXTERN int
lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds);
///@}

View File

@ -0,0 +1,224 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(__LWS_DISPLAY_H__)
#define __LWS_DISPLAY_H__
typedef int16_t lws_display_list_coord_t;
typedef uint16_t lws_display_scalar;
typedef uint16_t lws_display_rotation_t;
typedef uint32_t lws_display_colour_t;
typedef uint16_t lws_display_palette_idx_t;
typedef struct lws_box {
lws_fx_t x;
lws_fx_t y;
lws_fx_t w;
lws_fx_t h;
} lws_box_t;
struct lws_display_state;
struct lws_display;
typedef enum {
LWSSURF_TRUECOLOR32,
LWSSURF_565,
LWSSURF_PALETTE,
LWSSURF_QUANTIZED_4BPP
} lws_surface_type_t;
typedef struct lws_surface_info {
lws_fx_t wh_px[2];
lws_fx_t wh_mm[2];
const lws_display_colour_t *palette;
size_t palette_depth;
lws_surface_type_t type;
uint8_t greyscale:1; /* line: 0 = RGBA, 1 = YA */
uint8_t partial:1; /* can handle partial */
uint8_t render_to_rgba:1; /* render to 32-bit RGBA, not 24-bit RGB */
} lws_surface_info_t;
typedef struct lws_greyscale_error {
int16_t rgb[1];
} lws_greyscale_error_t;
typedef struct lws_colour_error {
int16_t rgb[3];
} lws_colour_error_t;
typedef union {
lws_greyscale_error_t grey; /* when ic->greyscale set */
lws_colour_error_t colour; /* when ic->greyscale == 0 */
} lws_surface_error_t;
LWS_VISIBLE LWS_EXTERN void
lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x,
const lws_display_colour_t *c);
LWS_VISIBLE LWS_EXTERN lws_display_palette_idx_t
lws_display_palettize_grey(const lws_surface_info_t *ic,
const lws_display_colour_t *palette, size_t pdepth,
lws_display_colour_t c, lws_greyscale_error_t *ectx);
LWS_VISIBLE LWS_EXTERN lws_display_palette_idx_t
lws_display_palettize_col(const lws_surface_info_t *ic,
const lws_display_colour_t *palette, size_t pdepth,
lws_display_colour_t c, lws_colour_error_t *ectx);
/*
* This is embedded in the actual display implementation object at the top,
* so a pointer to this can be cast to a pointer to the implementation object
* by any code that is specific to how it was implemented.
*
* Notice for the backlight / display intensity we contain pwm_ops... these can
* be some other pwm_ops like existing gpio pwm ops, or handled in a customized
* way like set oled contrast. Either way, the pwm level is arrived at via a
* full set of lws_led_sequences capable of generic lws transitions
*/
typedef struct lws_display {
int (*init)(struct lws_display_state *lds);
const lws_pwm_ops_t *bl_pwm_ops;
int (*contrast)(struct lws_display_state *lds, uint8_t contrast);
int (*blit)(struct lws_display_state *lds, const uint8_t *src,
lws_box_t *box, lws_dll2_owner_t *ids);
int (*power)(struct lws_display_state *lds, int state);
const lws_led_sequence_def_t *bl_active;
const lws_led_sequence_def_t *bl_dim;
const lws_led_sequence_def_t *bl_transition;
const char *name;
void *variant;
int bl_index;
lws_surface_info_t ic;
uint16_t latency_wake_ms;
/**< ms required after wake from sleep before display usable again...
* delay bringing up the backlight for this amount of time on wake.
* This is managed via a sul on the event loop, not blocking. */
uint16_t latency_update_ms;
/**< nominal update latency in ms */
} lws_display_t;
/*
* This contains dynamic data related to display state
*/
enum lws_display_controller_state {
LWSDISPS_OFF,
LWSDISPS_AUTODIMMED, /* is in pre- blanking static dim mode */
LWSDISPS_BECOMING_ACTIVE, /* waiting for wake latency before active */
LWSDISPS_ACTIVE, /* is active */
LWSDISPS_GOING_OFF /* dimming then off */
};
typedef struct lws_display_state {
lws_sorted_usec_list_t sul_autodim;
char current_url[96];
const lws_display_t *disp;
struct lws_context *ctx;
void *priv; /* subclass driver alloc'd priv */
int autodim_ms;
int off_ms;
struct lws_led_state *bl_lcs;
lws_led_state_chs_t chs;
/* set of sequencer transition channels */
enum lws_display_controller_state state;
char display_busy;
} lws_display_state_t;
/* Used for async display driver events, eg, EPD refresh completion */
typedef int (*lws_display_completion_t)(lws_display_state_t *lds, int a);
/**
* lws_display_state_init() - initialize display states
*
* \param lds: the display state object
* \param ctx: the lws context
* \param autodim_ms: ms since last active report to dim display (<0 = never)
* \param off_ms: ms since dim to turn display off (<0 = never)
* \param bl_lcs: the led controller instance that has the backlight
* \param disp: generic display object we belong to
*
* This initializes a display's state, and sets up the optional screen auto-dim
* and blanking on inactive, and gradual brightness change timer.
*
* - auto-dim then off: set autodim to some ms and off_ms to some ms
* - auto-dim only: set autodim to some ms and off_ms to -1
* - off-only: set autodim to some ms and off_ms to 0
* - neither: set both autodim and off_ms to -1
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_state_init(lws_display_state_t *lds, struct lws_context *ctx,
int autodim_ms, int off_ms, struct lws_led_state *bl_lcs,
const lws_display_t *disp);
/**
* lws_display_state_set_brightness() - gradually change the brightness
*
* \param lds: the display state we are changing
* \param target: the target brightness to transition to
*
* Adjusts the brightness gradually twoards the target at 20Hz
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_state_set_brightness(lws_display_state_t *lds,
const lws_led_sequence_def_t *pwmseq);
/*
* lws_display_state_active() - inform the system the display is active
*
* \param lds: the display state we are marking as active
*
* Resets the auto-dim and auto-off timers and makes sure the display is on and
* at the active brightness level
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_state_active(lws_display_state_t *lds);
/*
* lws_display_state_off() - turns off the related display
*
* \param lds: the display state we are turning off
*
* Turns the display to least power mode or completely off if possible.
* Disables the timers related to dimming and blanking.
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_state_off(lws_display_state_t *lds);
#endif

View File

@ -0,0 +1,308 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup ll linked-lists
* ##Linked list apis
*
* simple single and doubly-linked lists
*/
///@{
/**
* lws_start_foreach_ll(): linkedlist iterator helper start
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_ll().
*/
#define lws_start_foreach_ll(type, it, start)\
{ \
type it = start; \
while (it) {
/**
* lws_end_foreach_ll(): linkedlist iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_ll() that ends the
* while loop.
*/
#define lws_end_foreach_ll(it, nxt) \
it = it->nxt; \
} \
}
/**
* lws_start_foreach_ll_safe(): linkedlist iterator helper start safe against delete
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
* \param nxt: member name in the iterator pointing to next list element
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_end_foreach_ll_safe().
* Performs storage of next increment for situations where iterator can become invalidated
* during iteration.
*/
#define lws_start_foreach_ll_safe(type, it, start, nxt)\
{ \
type it = start; \
while (it) { \
type next_##it = it->nxt;
/**
* lws_end_foreach_ll_safe(): linkedlist iterator helper end (pre increment storage)
*
* \param it: same iterator var name given when starting
*
* This helper is the partner for lws_start_foreach_ll_safe() that ends the
* while loop. It uses the precreated next_ variable already stored during
* start.
*/
#define lws_end_foreach_ll_safe(it) \
it = next_##it; \
} \
}
/**
* lws_start_foreach_llp(): linkedlist pointer iterator helper start
*
* \param type: type of iteration, eg, struct xyz **
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at the
* address of start and ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_llp().
*
* This helper variant iterates using a pointer to the previous linked-list
* element. That allows you to easily delete list members by rewriting the
* previous pointer to the element's next pointer.
*/
#define lws_start_foreach_llp(type, it, start)\
{ \
type it = &(start); \
while (*(it)) {
#define lws_start_foreach_llp_safe(type, it, start, nxt)\
{ \
type it = &(start); \
type next; \
while (*(it)) { \
next = &((*(it))->nxt); \
/**
* lws_end_foreach_llp(): linkedlist pointer iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_llp() that ends the
* while loop.
*/
#define lws_end_foreach_llp(it, nxt) \
it = &(*(it))->nxt; \
} \
}
#define lws_end_foreach_llp_safe(it) \
it = next; \
} \
}
#define lws_ll_fwd_insert(\
___new_object, /* pointer to new object */ \
___m_list, /* member for next list object ptr */ \
___list_head /* list head */ \
) {\
___new_object->___m_list = ___list_head; \
___list_head = ___new_object; \
}
#define lws_ll_fwd_remove(\
___type, /* type of listed object */ \
___m_list, /* member for next list object ptr */ \
___target, /* object to remove from list */ \
___list_head /* list head */ \
) { \
lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
if (*___ppss == ___target) { \
*___ppss = ___target->___m_list; \
break; \
} \
} lws_end_foreach_llp(___ppss, ___m_list); \
}
/*
* doubly linked-list
*/
/*
* lws_dll2_owner / lws_dll2 : more capable version of lws_dll. Differences:
*
* - there's an explicit lws_dll2_owner struct which holds head, tail and
* count of members.
*
* - list members all hold a pointer to their owner. So user code does not
* have to track anything about exactly what lws_dll2_owner list the object
* is a member of.
*
* - you can use lws_dll unless you want the member count or the ability to
* not track exactly which list it's on.
*
* - layout is compatible with lws_dll (but lws_dll apis will not update the
* new stuff)
*/
struct lws_dll2;
struct lws_dll2_owner;
typedef struct lws_dll2 {
struct lws_dll2 *prev;
struct lws_dll2 *next;
struct lws_dll2_owner *owner;
} lws_dll2_t;
typedef struct lws_dll2_owner {
struct lws_dll2 *tail;
struct lws_dll2 *head;
uint32_t count;
} lws_dll2_owner_t;
LWS_VISIBLE LWS_EXTERN int
lws_dll2_is_detached(const struct lws_dll2 *d);
static LWS_INLINE const struct lws_dll2_owner *
lws_dll2_owner(const struct lws_dll2 *d) { return d->owner; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_head(struct lws_dll2_owner *owner) { return owner->head; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_tail(struct lws_dll2_owner *owner) { return owner->tail; }
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_remove(struct lws_dll2 *d);
typedef int (*lws_dll2_foreach_cb_t)(struct lws_dll2 *d, void *user);
LWS_VISIBLE LWS_EXTERN int
lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user,
lws_dll2_foreach_cb_t cb);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_clear(struct lws_dll2 *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_owner_clear(struct lws_dll2_owner *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_insert(struct lws_dll2 *d, struct lws_dll2 *prev);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i));
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted_priv(lws_dll2_t *d, lws_dll2_owner_t *own, void *priv,
int (*compare3)(void *priv, const lws_dll2_t *d,
const lws_dll2_t *i));
LWS_VISIBLE LWS_EXTERN void *
_lws_dll2_search_sz_pl(lws_dll2_owner_t *own, const char *name, size_t namelen,
size_t dll2_ofs, size_t ptr_ofs);
/*
* Searches objects in an owner list linearly and returns one with a given
* member C-string matching a supplied length-provided string if it exists, else
* NULL.
*/
#define lws_dll2_search_sz_pl(own, name, namelen, type, membd2list, membptr) \
((type *)_lws_dll2_search_sz_pl(own, name, namelen, \
offsetof(type, membd2list), \
offsetof(type, membptr)))
#if defined(_DEBUG)
void
lws_dll2_describe(struct lws_dll2_owner *owner, const char *desc);
#else
#define lws_dll2_describe(x, y)
#endif
/*
* these are safe against the current container object getting deleted,
* since the hold his next in a temp and go to that next. ___tmp is
* the temp.
*/
#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
{ \
___type ___it = ___start; \
while (___it) { \
___type ___tmp = (___it)->next;
#define lws_end_foreach_dll_safe(___it, ___tmp) \
___it = ___tmp; \
} \
}
#define lws_start_foreach_dll(___type, ___it, ___start) \
{ \
___type ___it = ___start; \
while (___it) {
#define lws_end_foreach_dll(___it) \
___it = (___it)->next; \
} \
}
///@}

View File

@ -0,0 +1,524 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws display_list and display_list objects (dlo)
*/
#if !defined(__LWS_DLO_H__)
#define __LWS_DLO_H__
#include <stdint.h>
struct lws_display_render_state;
struct lws_surface_info;
struct lws_display_state;
struct lws_display_font;
struct lws_dlo_text;
struct lws_display;
struct lws_dlo_text;
struct lws_dlo;
#define LWSDC_RGBA(_r, _g, _b, _a) (((uint32_t)(_r) & 0xff) | \
(((uint32_t)(_g) & 0xff) << 8) | \
(((uint32_t)(_b) & 0xff) << 16) | \
(((uint32_t)(_a) & 0xff) << 24))
#define LWSDC_R(_c) ((_c) & 0xff)
#define LWSDC_G(_c) ((_c >> 8) & 0xff)
#define LWSDC_B(_c) ((_c >> 16) & 0xff)
#define LWSDC_ALPHA(_c) ((_c >> 24) & 0xff)
#define RGB_TO_Y(_r, _g, _b) ((((_r) * 299) + ((_g) * 587) + ((_b) * 114)) / 1000)
/* stores Y in RGBY */
#define PALETTE_RGBY(_r, _g, _b) LWSDC_RGBA(_r, _g, _b, (RGB_TO_Y(_r, _g, _b)))
typedef struct {
lws_fx_t w;
lws_fx_t h;
} lws_dlo_dim_t;
/*
* When using RGBA to describe native greyscale, R is Y and A is A, GB is ignored
*/
/* composed at start of larger, font-specific glyph struct */
typedef struct lws_font_glyph {
lws_dll2_t list;
lws_fx_t xorg;
lws_fx_t xpx;
lws_fx_t height;
lws_fx_t cwidth;
int8_t x; /* x offset inside the glyph */
} lws_font_glyph_t;
typedef lws_stateful_ret_t (*lws_dlo_renderer_t)(struct lws_display_render_state *rs);
typedef lws_font_glyph_t * (*lws_dlo_image_glyph_t)(
struct lws_dlo_text *text,
uint32_t unicode, char attach);
typedef void (*lws_dlo_destroy_t)(struct lws_dlo *dlo);
typedef struct lws_display_id {
lws_dll2_t list;
char id[16];
lws_box_t box; /* taken from DLO after layout */
void *priv_user;
void *priv_driver;
char exists;
char iframe; /* 1 = render html as if partial
* is the origin, otherwise
* render html with surface
* (0,0) as origin and rs->box
* is a viewport on to that */
} lws_display_id_t;
/*
* Common dlo object that joins the display list, composed into a subclass
* object like lws_dlo_rect_t etc
*/
typedef struct lws_dlo {
lws_dll2_t list;
lws_dll2_t col_list; /* lws_dlo_t: column-mates */
lws_dll2_t row_list; /* lws_dlo_t: row-mates */
/* children are rendered "inside" the parent DLO box after allowing
* for parent padding */
lws_dll2_owner_t children;
/* only used for dlo rect representing whole table */
lws_dll2_owner_t table_cols; /* lhp_table_col_t */
lws_dll2_owner_t table_rows; /* lhp_table_row_t */
/* may point to dlo whose width or height decides our x or y */
struct lws_dlo *abut_x;
struct lws_dlo *abut_y;
lws_dlo_destroy_t _destroy; /* dlo-type specific cb */
lws_dlo_renderer_t render; /* dlo-type specific cb */
lws_fx_t margin[4];
lws_fx_t padding[4]; /* child origin */
lws_display_id_t *id; /* only valid until ids destroyed */
lws_box_t box;
lws_display_colour_t dc;
uint8_t flag_runon:1; /* continues same line */
uint8_t flag_done_align:1;
uint8_t flag_toplevel:1; /* don't scan up with me (different owner) */
/* render-specific members ... */
} lws_dlo_t;
typedef struct lws_circle {
lws_fx_t r;
/* rasterization temps */
lws_fx_t orx; /* abs pixel x for centre */
lws_fx_t ory; /* abs pixel y for centre */
lws_fx_t rsq;
lws_fx_t ys;
} lws_circle_t;
typedef struct lws_dlo_rect {
lws_dlo_t dlo;
lws_circle_t c[4]; /* t-l, t-r, b-l, b-r */
lws_fx_t b[4]; /* border width on t/r/b/l */
lws_display_colour_t dcb; /* border colour */
/* rasterization temps */
lws_fx_t btm;
lws_fx_t right;
lws_box_t db;
uint8_t init;
uint8_t alt;
} lws_dlo_rect_t;
typedef struct lws_dlo_circle {
lws_dlo_t dlo;
} lws_dlo_circle_t;
typedef struct lws_font_choice {
const char *family_name;
const char *generic_name;
uint16_t weight;
uint16_t style; /* normal, italic, oblique */
uint16_t fixed_height;
} lws_font_choice_t;
typedef struct lws_display_font {
lws_dll2_t list;
lws_font_choice_t choice;
const uint8_t *data; /* may be cast to imp struct */
uint8_t *priv; /* only used by implementation */
size_t data_len;
lws_dlo_renderer_t renderer;
lws_dlo_image_glyph_t image_glyph;
lws_fx_t em; /* 1 em in pixels */
lws_fx_t ex; /* 1 ex in pixels */
} lws_display_font_t;
typedef struct lws_dlo_filesystem {
lws_dll2_t list;
const char *name;
const void *data;
size_t len;
} lws_dlo_filesystem_t;
#define LWSDLO_TEXT_FLAG_WRAP (1 << 0)
typedef struct lws_dlo_text {
lws_dlo_t dlo;
const lws_display_font_t *font;
lws_dll2_owner_t glyphs;
lws_box_t bounding_box; /* { 0, 0, w, h } relative
* to and subject to
* clipping by .dlo.box */
/* referred to by glyphs */
const struct lws_surface_info *ic;
struct lwsac *ac_glyphs;
uint8_t *line;
uint16_t curr;
char *text;
uint8_t *kern;
size_t text_len;
lws_display_list_coord_t clkernpx;
lws_display_list_coord_t cwidth;
lws_fx_t indent;
uint32_t flags;
int16_t font_y_baseline;
int16_t font_height;
int16_t font_line_height;
int16_t group_height;
int16_t group_y_baseline;
lws_fx_t _cwidth;
} lws_dlo_text_t;
typedef struct lws_dlo_rasterize {
lws_dll2_owner_t owner; /* lws_flow_t */
lws_sorted_usec_list_t sul;
int lines;
} lws_dlo_rasterize_t;
typedef struct lws_dlo_png {
lws_dlo_t dlo; /* ordering: first */
lws_flow_t flow; /* ordering: second */
lws_upng_t *png;
} lws_dlo_png_t;
typedef struct lws_dlo_jpeg {
lws_dlo_t dlo; /* ordering: first */
lws_flow_t flow; /* ordering: second */
lws_jpeg_t *j;
} lws_dlo_jpeg_t;
typedef enum {
LWSDLOSS_TYPE_JPEG,
LWSDLOSS_TYPE_PNG,
LWSDLOSS_TYPE_CSS,
} lws_dlo_image_type_t;
typedef struct {
union {
lws_dlo_jpeg_t *dlo_jpeg;
lws_dlo_png_t *dlo_png;
} u;
lws_dlo_image_type_t type;
char failed;
} lws_dlo_image_t;
typedef struct lws_displaylist {
lws_dll2_owner_t dl;
struct lws_display_state *ds;
} lws_displaylist_t;
typedef struct lws_dl_rend {
lws_displaylist_t *dl;
int w;
int h;
} lws_dl_rend_t;
typedef struct lws_display_render_stack {
lws_dlo_t *dlo; /* position in dlo owner */
lws_box_t co; /* our origin as parent */
} lws_display_render_stack_t;
typedef struct lws_display_render_state {
lws_sorted_usec_list_t sul; /* return to event loop statefully */
struct lws_display_state *lds; /* optional, if using lws_display */
lws_dll2_owner_t ids;
const struct lws_surface_info *ic; /* display dimensions, palette */
lws_display_render_stack_t st[12]; /* DLO child stack */
int sp; /* DLO child stack level */
uint8_t *line; /* Y or RGB line comp buffer */
lws_displaylist_t displaylist;
lws_display_scalar curr;
lws_display_scalar lowest_id_y;
char html;
} lws_display_render_state_t;
LWS_VISIBLE LWS_EXTERN void
lws_display_render_free_ids(lws_display_render_state_t *rs);
LWS_VISIBLE LWS_EXTERN lws_display_id_t *
lws_display_render_add_id(lws_display_render_state_t *rs, const char *id, void *priv);
LWS_VISIBLE LWS_EXTERN lws_display_id_t *
lws_display_render_get_id(lws_display_render_state_t *rs, const char *id);
LWS_VISIBLE LWS_EXTERN void
lws_display_render_dump_ids(lws_dll2_owner_t *ids);
LWS_VISIBLE LWS_EXTERN void
lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim);
LWS_VISIBLE LWS_EXTERN void
lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim);
/**
* lws_display_dl_init() - init display list object
*
* \param dl: Pointer to the display list
* \param ds: Lws display state to bind the list to
*
* Initializes the display list \p dl and binds it to the display state \p ds.
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_dl_init(lws_displaylist_t *dl, struct lws_display_state *ds);
//#if defined(_DEBUG)
LWS_VISIBLE LWS_EXTERN void
lws_display_dl_dump(lws_displaylist_t *dl);
//#endif
/**
* lws_display_list_destroy() - destroys display list and objects on it
*
* \param dl: Pointer to the display list
*
* Destroys every DLO on the list.
*/
LWS_VISIBLE LWS_EXTERN void
lws_display_list_destroy(lws_displaylist_t *dl);
LWS_VISIBLE LWS_EXTERN void
lws_display_dlo_destroy(lws_dlo_t **r);
LWS_VISIBLE LWS_EXTERN int
lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo);
LWS_VISIBLE LWS_EXTERN int
lws_dlo_ensure_err_diff(lws_dlo_t *dlo);
/*
* lws_display_list_render_line() - render a single raster line of the list
*
* \param rs: prepared render state object
*
* Allocates a line pair buffer into ds->line if necessary, and renders the
* current line (set by ds->curr) of the display list rasterization into it
*/
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_list_render_line(lws_display_render_state_t *rs);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_get_ids_boxes(lws_display_render_state_t *rs);
/*
* rect
*/
LWS_VISIBLE LWS_EXTERN lws_dlo_rect_t *
lws_display_dlo_rect_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_fx_t *radii,
lws_display_colour_t dc);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_render_rect(struct lws_display_render_state *rs);
/*
* dlo text
*/
LWS_VISIBLE LWS_EXTERN lws_dlo_text_t *
lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_display_font_t *font);
LWS_VISIBLE LWS_EXTERN int
lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc,
lws_fx_t indent, const char *utf8, size_t text_len);
LWS_VISIBLE LWS_EXTERN void
lws_display_dlo_text_destroy(struct lws_dlo *dlo);
/*
* PNG
*/
LWS_VISIBLE LWS_EXTERN lws_dlo_png_t *
lws_display_dlo_png_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_render_png(struct lws_display_render_state *rs);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_dlo_png_metadata_scan(lws_dlo_png_t *dp);
LWS_VISIBLE LWS_EXTERN void
lws_display_dlo_png_destroy(struct lws_dlo *dlo);
/*
* JPEG
*/
LWS_VISIBLE LWS_EXTERN lws_dlo_jpeg_t *
lws_display_dlo_jpeg_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_render_jpeg(struct lws_display_render_state *rs);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_display_dlo_jpeg_metadata_scan(lws_dlo_jpeg_t *dj);
LWS_VISIBLE LWS_EXTERN void
lws_display_dlo_jpeg_destroy(struct lws_dlo *dlo);
/*
* SS / dlo images
*/
struct lhp_ctx;
typedef struct {
struct lws_context *cx;
lws_displaylist_t *dl;
lws_dlo_t *dlo_parent;
lws_box_t *box;
sul_cb_t on_rx;
lws_sorted_usec_list_t *on_rx_sul;
const char *url;
struct lhp_ctx *lhp;
lws_dlo_image_t *u;
int32_t window;
uint8_t type;
} lws_dlo_ss_create_info_t;
LWS_VISIBLE LWS_EXTERN int
lws_dlo_ss_create(lws_dlo_ss_create_info_t *i, lws_dlo_t **pdlo);
LWS_VISIBLE LWS_EXTERN int
lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u);
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lhp_displaylist_layout(struct lhp_ctx *ctx, char reason);
#define lws_dlo_image_width(_u) ((_u)->failed ? -1 : \
((_u)->type == LWSDLOSS_TYPE_JPEG ? \
(int)lws_jpeg_get_width((_u)->u.dlo_jpeg->j) : \
(int)lws_upng_get_width((_u)->u.dlo_png->png)))
#define lws_dlo_image_height(_u) ((_u)->failed ? -1 : \
((_u)->type == LWSDLOSS_TYPE_JPEG ? \
(int)lws_jpeg_get_height((_u)->u.dlo_jpeg->j) : \
(int)lws_upng_get_height((_u)->u.dlo_png->png)))
#define lws_dlo_image_metadata_scan(_u) ((_u)->failed ? LWS_SRET_FATAL : \
((_u)->type == LWSDLOSS_TYPE_JPEG ? \
lws_display_dlo_jpeg_metadata_scan((_u)->u.dlo_jpeg) : \
lws_display_dlo_png_metadata_scan((_u)->u.dlo_png)))
/*
* Font registry
*
* Register fonts (currently, psfu) to the lws_context, and select the closest
* matching. Used to pick fonts from whatever CSS information is available.
*/
LWS_VISIBLE LWS_EXTERN int
lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len);
LWS_VISIBLE LWS_EXTERN const lws_display_font_t *
lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints);
LWS_VISIBLE LWS_EXTERN void
lws_fonts_destroy(struct lws_context *cx);
/*
* Static blob registry (built-in, name-accessible blobs)
*/
LWS_VISIBLE LWS_EXTERN lws_dlo_filesystem_t *
lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f);
/* only needed if f dynamically heap-allocated... doesn't free data; data
* is typically overallocated after the lws_dlo_filesystem_t and freed when
* that is freed by this. */
LWS_VISIBLE LWS_EXTERN void
lws_dlo_file_unregister(lws_dlo_filesystem_t **f);
LWS_VISIBLE LWS_EXTERN void
lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name);
LWS_VISIBLE LWS_EXTERN const lws_dlo_filesystem_t *
lws_dlo_file_choose(struct lws_context *cx, const char *name);
LWS_VISIBLE LWS_EXTERN void
lws_dlo_file_destroy(struct lws_context *cx);
LWS_VISIBLE extern const struct lws_plat_file_ops lws_dlo_fops;
#endif

View File

@ -0,0 +1,169 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* lws_dsh (Disordered Shared Heap) is an opaque abstraction supporting a single
* linear buffer (overallocated at end of the lws_dsh_t) which may contain
* multiple kinds of packets that are retired out of order, and tracked by kind.
*
* Each kind of packet has an lws_dll2 list of its kind of packets and acts as
* a FIFO; packets of a particular type are always retired in order. But there
* is no requirement about the order types are retired matching the original
* order they arrived.
*
* Gaps are tracked as just another kind of "packet" list.
*
* "allocations" (including gaps) are prepended by an lws_dsh_object_t.
*
* dsh may themselves be on an lws_dll2_owner list, and under memory pressure
* allocate into other buffers on the list.
*
* All management structures exist inside the allocated buffer.
*/
enum {
LWS_DSHFLAG_ENABLE_COALESCE = (1u << 24),
LWS_DSHFLAG_ENABLE_SPLIT = (1u << 25),
};
/**
* lws_dsh_create() - Allocate a DSH buffer
*
* \param owner: the owning list this dsh belongs on, or NULL if standalone
* \param buffer_size: the allocation in bytes
* \param count_kinds: how many separately-tracked fifos use the buffer, or-ed
* with optional LWS_DSHFLAGs
*
* This makes a single heap allocation that includes internal tracking objects
* in the buffer. Sub-allocated objects are bound to a "kind" index and
* managed via a FIFO for each kind.
*
* Every "kind" of allocation shares the same buffer space.
*
* Multiple buffers may be bound together in an lws_dll2 list, and if an
* allocation cannot be satisfied by the local buffer, space can be borrowed
* from other dsh in the same list (the local dsh FIFO tracks these "foreign"
* allocations as if they were local).
*
* Returns an opaque pointer to the dsh, or NULL if allocation failed.
*/
LWS_VISIBLE LWS_EXTERN struct lws_dsh *
lws_dsh_create(lws_dll2_owner_t *owner, size_t buffer_size, int count_kinds);
LWS_VISIBLE LWS_EXTERN void
lws_dsh_empty(struct lws_dsh *dsh);
/**
* lws_dsh_destroy() - Destroy a DSH buffer
*
* \param pdsh: pointer to the dsh pointer
*
* Deallocates the DSH and sets *pdsh to NULL.
*
* Before destruction, any foreign buffer usage on the part of this dsh are
* individually freed. All dsh on the same list are walked and checked if they
* have their own foreign allocations on the dsh buffer being destroyed. If so,
* it attempts to migrate the allocation to a dsh that is not currently being
* destroyed. If all else fails (basically the buffer memory is being shrunk)
* unmigratable objects are cleanly destroyed.
*/
LWS_VISIBLE LWS_EXTERN void
lws_dsh_destroy(struct lws_dsh **pdsh);
/**
* lws_dsh_alloc_tail() - make a suballocation inside a dsh
*
* \param dsh: the dsh tracking the allocation
* \param kind: the kind of allocation
* \param src1: the first source data to copy
* \param size1: the size of the first source data
* \param src2: the second source data to copy (after the first), or NULL
* \param size2: the size of the second source data
*
* Allocates size1 + size2 bytes in a dsh (it prefers the given dsh but will
* borrow space from other dsh on the same list if necessary) and copies size1
* bytes into it from src1, followed by size2 bytes from src2 if src2 isn't
* NULL. The actual suballocation is a bit larger because of alignment and a
* prepended management header.
*
* The suballocation is added to the kind-specific FIFO at the tail.
*/
LWS_VISIBLE LWS_EXTERN int
lws_dsh_alloc_tail(struct lws_dsh *dsh, int kind, const void *src1,
size_t size1, const void *src2, size_t size2);
/**
* lws_dsh_free() - free a suballocation from the dsh
*
* \param obj: a pointer to a void * that pointed to the allocated payload
*
* This returns the space used by \p obj in the dsh buffer to the free list
* of the dsh the allocation came from.
*/
LWS_VISIBLE LWS_EXTERN void
lws_dsh_free(void **obj);
/**
* lws_dsh_consume() - partially consume a dsh
*
* \param dsh: the dsh
* \param kind: the kind of allocation (0 +)
* \param len: length to consume
*
* Consume part of a dsh object.
*/
LWS_VISIBLE LWS_EXTERN void
lws_dsh_consume(struct lws_dsh *dsh, int kind, size_t len);
LWS_VISIBLE LWS_EXTERN size_t
lws_dsh_get_size(struct lws_dsh *dsh, int kind);
/**
* lws_dsh_get_head() - get the head allocation inside the dsh
*
* \param dsh: the dsh tracking the allocation
* \param kind: the kind of allocation
* \param obj: pointer to a void * to be set to the payload
* \param size: set to the size of the allocation
*
* This gets the "next" object in the kind FIFO for the dsh, and returns 0 if
* any. If none, returns nonzero.
*
* This is nondestructive of the fifo or the payload. Use lws_dsh_free on
* obj to remove the entry from the kind fifo and return the payload to the
* free list.
*/
LWS_VISIBLE LWS_EXTERN int
lws_dsh_get_head(struct lws_dsh *dsh, int kind, void **obj, size_t *size);
/**
* lws_dsh_describe() - DEBUG BUILDS ONLY dump the dsh to the logs
*
* \param dsh: the dsh to dump
* \param desc: text that appears at the top of the dump
*
* Useful information for debugging lws_dsh
*/
LWS_VISIBLE LWS_EXTERN void
lws_dsh_describe(struct lws_dsh *dsh, const char *desc);

View File

@ -0,0 +1,52 @@
/*
* SPI - esp32 esp-idf api implementation
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for gpio, a real implementation provides
* functions for the ops that use the underlying OS gpio arrangements.
*/
#if defined(ESP_PLATFORM)
#define lws_esp32_spi_ops \
.init = lws_esp32_spi_init, \
.queue = lws_esp32_spi_queue, \
.alloc_dma = lws_esp32_spi_alloc_dma, \
.free_dma = lws_esp32_spi_free_dma, \
.in_flight = lws_esp32_spi_in_flight
LWS_VISIBLE LWS_EXTERN int
lws_esp32_spi_init(const lws_spi_ops_t *spi_ops);
LWS_VISIBLE LWS_EXTERN int
lws_esp32_spi_queue(const lws_spi_ops_t *spi_ops, const lws_spi_desc_t *desc);
LWS_VISIBLE LWS_EXTERN void *
lws_esp32_spi_alloc_dma(const struct lws_spi_ops *ctx, size_t size);
LWS_VISIBLE LWS_EXTERN void
lws_esp32_spi_free_dma(const struct lws_spi_ops *ctx, void **p);
LWS_VISIBLE LWS_EXTERN int
lws_esp32_spi_in_flight(const struct lws_spi_ops *ctx);
#endif

View File

@ -0,0 +1,153 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* These are exports needed by event lib plugins.
*/
enum lws_event_lib_ops_flags {
LELOF_ISPOLL = (1 >> 0),
LELOF_DESTROY_FINAL = (1 >> 1),
};
enum {
LWS_EV_READ = (1 << 0),
LWS_EV_WRITE = (1 << 1),
LWS_EV_START = (1 << 2),
LWS_EV_STOP = (1 << 3),
};
struct lws_event_loop_ops {
const char *name;
/* event loop-specific context init during context creation */
int (*init_context)(struct lws_context *context,
const struct lws_context_creation_info *info);
/* called during lws_destroy_context */
int (*destroy_context1)(struct lws_context *context);
/* called during lws_destroy_context2 */
int (*destroy_context2)(struct lws_context *context);
/* init vhost listening wsi */
int (*init_vhost_listen_wsi)(struct lws *wsi);
/* init the event loop for a pt */
int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
/* called at end of first phase of close_free_wsi() */
int (*wsi_logical_close)(struct lws *wsi);
/* return nonzero if client connect not allowed */
int (*check_client_connect_ok)(struct lws *wsi);
/* close handle manually */
void (*close_handle_manually)(struct lws *wsi);
/* event loop accept processing */
int (*sock_accept)(struct lws *wsi);
/* control wsi active events */
void (*io)(struct lws *wsi, unsigned int flags);
/* run the event loop for a pt */
void (*run_pt)(struct lws_context *context, int tsi);
/* called before pt is destroyed */
void (*destroy_pt)(struct lws_context *context, int tsi);
/* called just before wsi is freed */
void (*destroy_wsi)(struct lws *wsi);
/* return nonzero if caller thread is not loop service thread */
int (*foreign_thread)(struct lws_context *context, int tsi);
/* optional: custom implementation for faking POLLIN for buffered.
* return nonzero if any wsi faked */
int (*fake_POLLIN_override)(struct lws_context *context, int tsi);
uint8_t flags;
uint16_t evlib_size_ctx;
uint16_t evlib_size_pt;
uint16_t evlib_size_vh;
uint16_t evlib_size_wsi;
};
LWS_VISIBLE LWS_EXTERN void *
lws_evlib_wsi_to_evlib_pt(struct lws *wsi);
LWS_VISIBLE LWS_EXTERN void *
lws_evlib_tsi_to_evlib_pt(struct lws_context *ctx, int tsi);
/*
* You should consider these opaque for normal user code.
*/
LWS_VISIBLE LWS_EXTERN void *
lws_realloc(void *ptr, size_t size, const char *reason);
LWS_VISIBLE LWS_EXTERN void
lws_vhost_destroy1(struct lws_vhost *vh);
LWS_VISIBLE LWS_EXTERN void
lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
const char *caller);
LWS_VISIBLE LWS_EXTERN int
lws_vhost_foreach_listen_wsi(struct lws_context *cx, void *arg,
lws_dll2_foreach_cb_t cb);
struct lws_context_per_thread;
LWS_VISIBLE LWS_EXTERN void
lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt);
#if !defined(wsi_from_fd) && !defined(WIN32) && !defined(_WIN32)
struct lws_context;
LWS_VISIBLE LWS_EXTERN struct lws *
wsi_from_fd(const struct lws_context *context, int fd);
#endif
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_forced_tsi(struct lws_context *context, int tsi);
LWS_VISIBLE LWS_EXTERN void
lws_context_destroy2(struct lws_context *context);
LWS_VISIBLE LWS_EXTERN void
lws_destroy_event_pipe(struct lws *wsi);
LWS_VISIBLE LWS_EXTERN void
__lws_close_free_wsi_final(struct lws *wsi);
#if LWS_MAX_SMP > 1
struct lws_mutex_refcount {
pthread_mutex_t lock;
pthread_t lock_owner;
const char *last_lock_reason;
char lock_depth;
char metadata;
};
LWS_VISIBLE LWS_EXTERN void
lws_mutex_refcount_assert_held(struct lws_mutex_refcount *mr);
LWS_VISIBLE LWS_EXTERN void
lws_mutex_refcount_init(struct lws_mutex_refcount *mr);
LWS_VISIBLE LWS_EXTERN void
lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr);
LWS_VISIBLE LWS_EXTERN void
lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason);
LWS_VISIBLE LWS_EXTERN void
lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr);
#endif

View File

@ -0,0 +1,251 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Fault injection api if built with LWS_WITH_SYS_FAULT_INJECTION
*/
typedef struct lws_xos {
uint64_t s[4];
} lws_xos_t;
/**
* lws_xos_init() - seed xoshiro256 PRNG
*
* \param xos: the prng state object to initialize
* \param seed: the 64-bit seed
*
* Initialize PRNG \xos with the starting state represented by \p seed
*/
LWS_VISIBLE LWS_EXTERN void
lws_xos_init(struct lws_xos *xos, uint64_t seed);
/**
* lws_xos() - get next xoshiro256 PRNG result and update state
*
* \param xos: the PRNG state to use
*
* Returns next 64-bit PRNG result. These are cheap to get,
* quite a white noise sequence, and completely deterministic
* according to the seed it was initialized with.
*/
LWS_VISIBLE LWS_EXTERN uint64_t LWS_WARN_UNUSED_RESULT
lws_xos(struct lws_xos *xos);
/**
* lws_xos_percent() - return 1 a given percent of the time on average
*
* \param xos: the PRNG state to use
* \param percent: chance in 100 of returning 1
*
* Returns 1 if next random % 100 is < \p percent, such that
* 100 always returns 1, 0 never returns 1, and the chance linearly scales
* inbetween
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_xos_percent(struct lws_xos *xos, int percent);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
enum {
LWSFI_ALWAYS,
LWSFI_DETERMINISTIC, /* do .count injections after .pre then stop */
LWSFI_PROBABILISTIC, /* .pre % chance of injection */
LWSFI_PATTERN, /* use .count bits in .pattern after .pre */
LWSFI_PATTERN_ALLOC, /* as _PATTERN, but .pattern is malloc'd */
LWSFI_RANGE /* pick a number between pre and count */
};
typedef struct lws_fi {
const char *name;
const uint8_t *pattern;
uint64_t pre;
uint64_t count;
uint64_t times; /* start at 0, tracks usage */
char type; /* LWSFI_* */
} lws_fi_t;
typedef struct lws_fi_ctx {
lws_dll2_owner_t fi_owner;
struct lws_xos xos;
const char *name;
} lws_fi_ctx_t;
/**
* lws_fi() - find out if we should perform the named fault injection this time
*
* \param fic: fault injection tracking context
* \param fi_name: name of fault injection
*
* This checks if the named fault is configured in the fi tracking context
* provided, if it is, then it will make a decision if the named fault should
* be applied this time, using the tracking in the named lws_fi_t.
*
* If the provided context has a parent, that is also checked for the named fi
* item recursively, with the first found being used to determine if to inject
* or not.
*
* If LWS_WITH_SYS_FAULT_INJECTION is not defined, then this always return 0.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi(const lws_fi_ctx_t *fic, const char *fi_name);
/**
* lws_fi_range() - get a random number from a range
*
* \param fic: fault injection tracking context
* \param fi_name: name of fault injection
* \param result: points to uint64_t to be set to the result
*
* This lets you get a random number from an externally-set range, set using a
* fault injection syntax like "myfault(123..456)". That will cause us to
* return a number between those two inclusive, from the seeded PRNG.
*
* This is useful when you used lws_fi() with its own fault name to decide
* whether to inject the fault, and then the code to cause the fault needs
* additional constrained pseudo-random fuzzing for, eg, delays before issuing
* the fault.
*
* Returns 0 if \p *result is set, else nonzero for failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi_range(const lws_fi_ctx_t *fic, const char *name, uint64_t *result);
/**
* lws_fi_add() - add an allocated copy of fault injection to a context
*
* \param fic: fault injection tracking context
* \param fi: the fault injection details
*
* This allocates a copy of \p fi and attaches it to the fault injection context
* \p fic. \p fi can go out of scope after this safely.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi);
/**
* lws_fi_remove() - remove an allocated copy of fault injection from a context
*
* \param fic: fault injection tracking context
* \param name: the fault injection name to remove
*
* This looks for the named fault injection and removes and destroys it from
* the specified fault injection context
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_remove(lws_fi_ctx_t *fic, const char *name);
/**
* lws_fi_import() - transfers all the faults from one context to another
*
* \param fic_dest: the fault context to receive the faults
* \param fic_src: the fault context that will be emptied out into \p fic_dest
*
* This is used to initialize created object fault injection contexts from
* the caller.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src);
/**
* lws_fi_inherit_copy() - attach copies of matching fault injection objects to dest
*
* \param fic_dest: destination Fault Injection context
* \param fic_src: parent fault context that may contain matching rules
* \param scope: the name of the path match required, eg, "vh"
* \param value: the dynamic name of our match, eg, "myvhost"
*
* If called with scope "vh" and value "myvhost", then matches faults starting
* "vh=myvhost/", strips that part of the name if it matches and makes a copy
* of the rule with the modified name attached to the destination Fault Injection
* context.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src,
const char *scope, const char *value);
/**
* lws_fi_destroy() - removes all allocated fault injection entries
*
* \param fic: fault injection tracking context
*
* This walks any allocated fault injection entries in \p fic and detaches and
* destroys them. It doesn't try to destroc \p fic itself, since this is
* not usually directly allocated.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_destroy(const lws_fi_ctx_t *fic);
/**
* lws_fi_deserialize() - adds fault in string form to Fault Injection Context
*
* \p fic: the fault injection context
* \p sers: the string serializing the desired fault details
*
* This turns a string like "ss=captive_portal_detect/wsi/dnsfail(10%)" into
* a fault injection struct added to the fault injection context \p fic
*
* You can prepare the context creation info .fic with these before creating
* the context, and use namespace paths on those to target other objects.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers);
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_wsi_fi(struct lws *wsi, const char *name);
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_context_fi(struct lws_context *ctx, const char *name);
#if defined(LWS_WITH_SECURE_STREAMS)
struct lws_ss_handle;
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name);
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
struct lws_sspc_handle;
LWS_VISIBLE LWS_EXTERN int
_lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name);
#endif
#endif
#define lws_fi_user_wsi_fi(_wsi, _name) _lws_fi_user_wsi_fi(_wsi, _name)
#define lws_fi_user_context_fi(_ctx, _name) _lws_fi_user_context_fi(_ctx, _name)
#define lws_fi_user_ss_fi(_h, _name) _lws_fi_user_ss_fi(_h, _name)
#define lws_fi_user_sspc_fi(_h, _name) _lws_fi_user_sspc_fi(_h, _name)
#else
/*
* Helper so we can leave lws_fi() calls embedded in the code being tested,
* if fault injection is not enabled then it just always says "no" at buildtime.
*/
#define lws_fi(_fi_name, _fic) (0)
#define lws_fi_destroy(_x)
#define lws_fi_inherit_copy(_a, _b, _c, _d)
#define lws_fi_deserialize(_x, _y)
#define lws_fi_user_wsi_fi(_wsi, _name) (0)
#define lws_fi_user_context_fi(_wsi, _name) (0)
#define lws_fi_user_ss_fi(_h, _name) (0)
#define lws_fi_user_sspc_fi(_h, _name) (0)
#endif

View File

@ -0,0 +1,87 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from libwebsockets.h if LWS_PLAT_FREERTOS
*/
typedef int lws_sockfd_type;
typedef int lws_filefd_type;
#if defined(LWS_AMAZON_RTOS)
#include <FreeRTOS.h>
#include <event_groups.h>
#include <string.h>
#include "timers.h"
#include <lwip/sockets.h>
/*
* Later lwip (at least 2.1.12) already defines these in its own headers
* protected by the same test as used here... if POLLIN / POLLOUT already exist
* then assume no need to declare those and struct pollfd.
*
* Older lwip needs these declarations done here.
*/
#if !defined(POLLIN) && !defined(POLLOUT)
struct pollfd {
lws_sockfd_type fd; /**< fd related to */
short events; /**< which POLL... events to respond to */
short revents; /**< which POLL... events occurred */
};
#define POLLIN 0x0001
#define POLLPRI 0x0002
#define POLLOUT 0x0004
#define POLLERR 0x0008
#define POLLHUP 0x0010
#define POLLNVAL 0x0020
#endif
#else /* LWS_AMAZON_RTOS */
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
//#include "esp_event_loop.h"
#include "nvs.h"
#include "driver/gpio.h"
#include "spi_flash_mmap.h"
#include "freertos/timers.h"
#if defined(LWS_ESP_PLATFORM)
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#if defined(LWS_WITH_DRIVERS)
#include "libwebsockets/lws-gpio.h"
extern const lws_gpio_ops_t lws_gpio_plat;
#endif
#endif
#endif /* LWS_AMAZON_RTOS */
#if !defined(CONFIG_FREERTOS_HZ)
#define CONFIG_FREERTOS_HZ 100
#endif

View File

@ -0,0 +1,215 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup search Search
*
* ##Full-text search
*
* Lws provides superfast indexing and fulltext searching from index files on
* storage.
*/
///@{
struct lws_fts;
struct lws_fts_file;
/*
* Queries produce their results in an lwsac, using these public API types.
* The first thing in the lwsac is always a struct lws_fts_result (see below)
* containing heads for linked-lists of the other result types.
*/
/* one filepath's results */
struct lws_fts_result_filepath {
struct lws_fts_result_filepath *next;
int matches; /* logical number of matches */
int matches_length; /* bytes in length table (may be zero) */
int lines_in_file;
int filepath_length;
/* - uint32_t line table follows (first for alignment) */
/* - filepath (of filepath_length) follows */
};
/* autocomplete result */
struct lws_fts_result_autocomplete {
struct lws_fts_result_autocomplete *next;
int instances;
int agg_instances;
int ac_length;
char elided; /* children skipped in interest of antecedent children */
char has_children;
/* - autocomplete suggestion (of length ac_length) follows */
};
/*
* The results lwsac always starts with this. If no results and / or no
* autocomplete the members may be NULL. This implies the symbol nor any
* suffix on it exists in the trie file.
*/
struct lws_fts_result {
struct lws_fts_result_filepath *filepath_head;
struct lws_fts_result_autocomplete *autocomplete_head;
int duration_ms;
int effective_flags; /* the search flags that were used */
};
/*
* index creation functions
*/
/**
* lws_fts_create() - Create a new index file
*
* \param fd: The fd opened for write
*
* Inits a new index file, returning a struct lws_fts to represent it
*/
LWS_VISIBLE LWS_EXTERN struct lws_fts *
lws_fts_create(int fd);
/**
* lws_fts_destroy() - Finalize a new index file / destroy the trie lwsac
*
* \param trie: The previously opened index being finalized
*
* Finalizes an index file that was being created, and frees the memory involved
* *trie is set to NULL afterwards.
*/
LWS_VISIBLE LWS_EXTERN void
lws_fts_destroy(struct lws_fts **trie);
/**
* lws_fts_file_index() - Create a new entry in the trie file for an input path
*
* \param t: The previously opened index being written
* \param filepath: The filepath (which may be virtual) associated with this file
* \param filepath_len: The number of chars in the filepath
* \param priority: not used yet
*
* Returns an ordinal that represents this new filepath in the index file.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fts_file_index(struct lws_fts *t, const char *filepath, int filepath_len,
int priority);
/**
* lws_fts_fill() - Process all or a bufferload of input file
*
* \param t: The previously opened index being written
* \param file_index: The ordinal representing this input filepath
* \param buf: A bufferload of data from the input file
* \param len: The number of bytes in buf
*
* Indexes a buffer of data from the input file.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fts_fill(struct lws_fts *t, uint32_t file_index, const char *buf,
size_t len);
/**
* lws_fts_serialize() - Store the in-memory trie into the index file
*
* \param t: The previously opened index being written
*
* The trie is held in memory where it can be added to... after all the input
* filepaths and data have been processed, this is called to serialize /
* write the trie data into the index file.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fts_serialize(struct lws_fts *t);
/*
* index search functions
*/
/**
* lws_fts_open() - Open an existing index file to search it
*
* \param filepath: The filepath to the index file to open
*
* Opening the index file returns an opaque struct lws_fts_file * that is
* used to perform other operations on it, or NULL if it can't be opened.
*/
LWS_VISIBLE LWS_EXTERN struct lws_fts_file *
lws_fts_open(const char *filepath);
#define LWSFTS_F_QUERY_AUTOCOMPLETE (1 << 0)
#define LWSFTS_F_QUERY_FILES (1 << 1)
#define LWSFTS_F_QUERY_FILE_LINES (1 << 2)
#define LWSFTS_F_QUERY_QUOTE_LINE (1 << 3)
struct lws_fts_search_params {
/* the actual search term */
const char *needle;
/* if non-NULL, FILE results for this filepath only */
const char *only_filepath;
/* will be set to the results lwsac */
struct lwsac *results_head;
/* combination of LWSFTS_F_QUERY_* flags */
int flags;
/* maximum number of autocomplete suggestions to return */
int max_autocomplete;
/* maximum number of filepaths to return */
int max_files;
/* maximum number of line number results to return per filepath */
int max_lines;
};
/**
* lws_fts_search() - Perform a search operation on an index
*
* \param jtf: The index file struct returned by lws_fts_open
* \param ftsp: The struct lws_fts_search_params filled in by the caller
*
* The caller should memset the ftsp struct to 0 to ensure members that may be
* introduced in later versions contain known values, then set the related
* members to describe the kind of search action required.
*
* ftsp->results_head is the results lwsac, or NULL. It should be freed with
* lwsac_free() when the results are finished with.
*
* Returns a pointer into the results lwsac that is a struct lws_fts_result
* containing the head pointers into linked-lists of results for autocomplete
* and filepath data, along with some sundry information. This does not need
* to be freed since freeing the lwsac will also remove this and everything it
* points to.
*/
LWS_VISIBLE LWS_EXTERN struct lws_fts_result *
lws_fts_search(struct lws_fts_file *jtf, struct lws_fts_search_params *ftsp);
/**
* lws_fts_close() - Close a previously-opened index file
*
* \param jtf: The pointer returned from the open
*
* Closes the file handle on the index and frees any allocations
*/
LWS_VISIBLE LWS_EXTERN void
lws_fts_close(struct lws_fts_file *jtf);
///@}

View File

@ -0,0 +1,170 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup generic AES
* ## Generic AES related functions
*
* Lws provides generic AES functions that abstract the ones
* provided by whatever tls library you are linking against.
*
* It lets you use the same code if you build against mbedtls or OpenSSL
* for example.
*/
///@{
#if defined(LWS_WITH_MBEDTLS)
#include <mbedtls/aes.h>
#include <mbedtls/gcm.h>
#endif
enum enum_aes_modes {
LWS_GAESM_CBC,
LWS_GAESM_CFB128,
LWS_GAESM_CFB8,
LWS_GAESM_CTR,
LWS_GAESM_ECB,
LWS_GAESM_OFB,
LWS_GAESM_XTS, /* care... requires double-length key */
LWS_GAESM_GCM,
LWS_GAESM_KW,
};
enum enum_aes_operation {
LWS_GAESO_ENC,
LWS_GAESO_DEC
};
enum enum_aes_padding {
LWS_GAESP_NO_PADDING,
LWS_GAESP_WITH_PADDING
};
/* include/libwebsockets/lws-jwk.h must be included before this */
#define LWS_AES_BLOCKSIZE 128
#define LWS_AES_CBC_BLOCKLEN 16
struct lws_genaes_ctx {
#if defined(LWS_WITH_MBEDTLS)
union {
mbedtls_aes_context ctx;
#if defined(MBEDTLS_CIPHER_MODE_XTS)
mbedtls_aes_xts_context ctx_xts;
#endif
mbedtls_gcm_context ctx_gcm;
} u;
#else
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
ENGINE *engine;
char init;
#endif
unsigned char tag[16];
struct lws_gencrypto_keyelem *k;
enum enum_aes_operation op;
enum enum_aes_modes mode;
enum enum_aes_padding padding;
int taglen;
char underway;
};
/** lws_genaes_create() - Create genaes AES context
*
* \param ctx: your struct lws_genaes_ctx
* \param op: LWS_GAESO_ENC or LWS_GAESO_DEC
* \param mode: one of LWS_GAESM_
* \param el: struct prepared with key element data
* \param padding: 0 = no padding, 1 = padding
* \param engine: if openssl engine used, pass the pointer here
*
* Creates an AES context with a key associated with it, formed from
* the key elements in \p el.
*
* Returns 0 for OK or nonzero for error.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
enum enum_aes_padding padding, void *engine);
/** lws_genaes_destroy() - Destroy genaes AES context
*
* \param ctx: your struct lws_genaes_ctx
* \param tag: NULL, or, GCM-only: buffer to receive tag
* \param tlen: 0, or, GCM-only: length of tag buffer
*
* Destroys any allocations related to \p ctx.
*
* For GCM only, up to tlen bytes of tag buffer will be set on exit.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen);
/** lws_genaes_crypt() - Encrypt or decrypt
*
* \param ctx: your struct lws_genaes_ctx
* \param in: input plaintext or ciphertext
* \param len: length of input (which is always length of output)
* \param out: output plaintext or ciphertext
* \param iv_or_nonce_ctr_or_data_unit_16: NULL, iv, nonce_ctr16, or data_unit16
* \param stream_block_16: pointer to 16-byte stream block for CTR mode only
* \param nc_or_iv_off: NULL or pointer to nc, or iv_off
* \param taglen: length of tag
*
* Encrypts or decrypts using the AES mode set when the ctx was created.
* The last three arguments have different meanings depending on the mode:
*
* KW CBC CFB128 CFB8 CTR ECB OFB XTS
* iv_or_nonce_ct.._unit_16 : iv iv iv iv nonce NULL iv dataunt
* stream_block_16 : NULL NULL NULL NULL stream NULL NULL NULL
* nc_or_iv_off : NULL NULL iv_off NULL nc_off NULL iv_off NULL
*
* For GCM:
*
* iv_or_nonce_ctr_or_data_unit_16 : iv
* stream_block_16 : pointer to tag
* nc_or_iv_off : set pointed-to size_t to iv length
* in : first call: additional data, subsequently
* : input data
* len : first call: add data length, subsequently
* : input / output length
*
* The length of the optional arg is always 16 if used, regardless of the mode.
*
* Returns 0 for OK or nonzero for error.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
uint8_t *out,
uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16,
size_t *nc_or_iv_off, int taglen);
///@}

View File

@ -0,0 +1,137 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* These are gencrypto-level constants... they are used by both JOSE and direct
* gencrypto code. However while JWK relies on these, using gencrypto apis has
* no dependency at all on any JOSE type.
*/
enum lws_gencrypto_kty {
LWS_GENCRYPTO_KTY_UNKNOWN,
LWS_GENCRYPTO_KTY_OCT,
LWS_GENCRYPTO_KTY_RSA,
LWS_GENCRYPTO_KTY_EC
};
/*
* Keytypes where the same element name is reused must all agree to put the
* same-named element at the same e[] index. It's because when used with jwk,
* we parse and store in incoming key data, but we may not be informed of the
* definitive keytype until the end.
*/
enum lws_gencrypto_oct_tok {
LWS_GENCRYPTO_OCT_KEYEL_K, /* note... same offset as AES K */
LWS_GENCRYPTO_OCT_KEYEL_COUNT
};
enum lws_gencrypto_rsa_tok {
LWS_GENCRYPTO_RSA_KEYEL_E,
LWS_GENCRYPTO_RSA_KEYEL_N,
LWS_GENCRYPTO_RSA_KEYEL_D, /* note... same offset as EC D */
LWS_GENCRYPTO_RSA_KEYEL_P,
LWS_GENCRYPTO_RSA_KEYEL_Q,
LWS_GENCRYPTO_RSA_KEYEL_DP,
LWS_GENCRYPTO_RSA_KEYEL_DQ,
LWS_GENCRYPTO_RSA_KEYEL_QI,
/* we don't actively use these if given, but may come from COSE */
LWS_GENCRYPTO_RSA_KEYEL_OTHER,
LWS_GENCRYPTO_RSA_KEYEL_RI,
LWS_GENCRYPTO_RSA_KEYEL_DI,
LWS_GENCRYPTO_RSA_KEYEL_TI,
LWS_GENCRYPTO_RSA_KEYEL_COUNT
};
enum lws_gencrypto_ec_tok {
LWS_GENCRYPTO_EC_KEYEL_CRV,
LWS_GENCRYPTO_EC_KEYEL_X,
/* note... same offset as RSA D */
LWS_GENCRYPTO_EC_KEYEL_D = LWS_GENCRYPTO_RSA_KEYEL_D,
LWS_GENCRYPTO_EC_KEYEL_Y,
LWS_GENCRYPTO_EC_KEYEL_COUNT
};
enum lws_gencrypto_aes_tok {
/* note... same offset as OCT K */
LWS_GENCRYPTO_AES_KEYEL_K = LWS_GENCRYPTO_OCT_KEYEL_K,
LWS_GENCRYPTO_AES_KEYEL_COUNT
};
/* largest number of key elements for any algorithm */
#define LWS_GENCRYPTO_MAX_KEYEL_COUNT LWS_GENCRYPTO_RSA_KEYEL_COUNT
/* this "stretchy" type holds individual key element data in binary form.
* It's typcially used in an array with the layout mapping the element index to
* the key element meaning defined by the enums above. An array of these of
* length LWS_GENCRYPTO_MAX_KEYEL_COUNT can define key elements for any key
* type.
*/
typedef struct lws_gencrypto_keyelem {
uint8_t *buf;
uint32_t len;
} lws_gc_elem_t;
/**
* lws_gencrypto_bits_to_bytes() - returns rounded up bytes needed for bits
*
* \param bits
*
* Returns the number of bytes needed to store the given number of bits. If
* a byte is partially used, the byte count is rounded up.
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_bits_to_bytes(int bits);
/**
* lws_base64_size() - returns estimated size of base64 encoding
*
* \param bytes
*
* Returns a slightly oversize estimate of the size of a base64 encoded version
* of the given amount of unencoded data.
*/
LWS_VISIBLE LWS_EXTERN int
lws_base64_size(int bytes);
/**
* lws_gencrypto_padded_length() - returns PKCS#5/#7 padded length
*
* @param blocksize - blocksize to pad to
* @param len - Length of input to pad
*
* Returns the length of a buffer originally of size len after PKCS#5 or PKCS#7
* padding has been applied to it.
*/
LWS_VISIBLE LWS_EXTERN size_t
lws_gencrypto_padded_length(size_t block_size, size_t len);

View File

@ -0,0 +1,211 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
enum enum_genec_alg {
LEGENEC_UNKNOWN,
LEGENEC_ECDH,
LEGENEC_ECDSA
};
struct lws_genec_ctx {
#if defined(LWS_WITH_MBEDTLS)
union {
mbedtls_ecdh_context *ctx_ecdh;
mbedtls_ecdsa_context *ctx_ecdsa;
} u;
#else
EVP_PKEY_CTX *ctx[2];
#endif
struct lws_context *context;
const struct lws_ec_curves *curve_table;
enum enum_genec_alg genec_alg;
char has_private;
};
#if defined(LWS_WITH_MBEDTLS)
enum enum_lws_dh_side {
LDHS_OURS = MBEDTLS_ECDH_OURS,
LDHS_THEIRS = MBEDTLS_ECDH_THEIRS
};
#else
enum enum_lws_dh_side {
LDHS_OURS,
LDHS_THEIRS
};
#endif
struct lws_ec_curves {
const char *name;
int tls_lib_nid;
uint16_t key_bytes;
};
/* ECDH-specific apis */
/** lws_genecdh_create() - Create a genecdh
*
* \param ctx: your genec context
* \param context: your lws_context (for RNG access)
* \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement
* struct lws_ec_curves array, terminated by an entry with
* .name = NULL, of curves you want to allow
*
* Initializes a genecdh
*/
LWS_VISIBLE int
lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table);
/** lws_genecdh_set_key() - Apply an EC key to our or theirs side
*
* \param ctx: your genecdh context
* \param el: your key elements
* \param side: LDHS_OURS or LDHS_THEIRS
*
* Applies an EC key to one side or the other of an ECDH ctx
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el,
enum enum_lws_dh_side side);
/** lws_genecdh_new_keypair() - Create a genec with a new public / private key
*
* \param ctx: your genec context
* \param side: LDHS_OURS or LDHS_THEIRS
* \param curve_name: an EC curve name, like "P-256"
* \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elems to take the new key
*
* Creates a genecdh with a newly minted EC public / private key
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
const char *curve_name, struct lws_gencrypto_keyelem *el);
LWS_VISIBLE LWS_EXTERN int
lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss,
int *ss_len);
/* ECDSA-specific apis */
/** lws_genecdsa_create() - Create a genecdsa and
*
* \param ctx: your genec context
* \param context: your lws_context (for RNG access)
* \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement
* struct lws_ec_curves array, terminated by an entry with
* .name = NULL, of curves you want to allow
*
* Initializes a genecdh
*/
LWS_VISIBLE int
lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table);
/** lws_genecdsa_new_keypair() - Create a genecdsa with a new public / private key
*
* \param ctx: your genec context
* \param curve_name: an EC curve name, like "P-256"
* \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elements to take the new key
*
* Creates a genecdsa with a newly minted EC public / private key
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
struct lws_gencrypto_keyelem *el);
/** lws_genecdsa_set_key() - Apply an EC key to an ecdsa context
*
* \param ctx: your genecdsa context
* \param el: your key elements
*
* Applies an EC key to an ecdsa context
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
const struct lws_gencrypto_keyelem *el);
/** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: unencrypted payload (usually a recomputed hash)
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param keybits: number of bits in the crypto key
* \param sig: pointer to the signature we received with the payload
* \param sig_len: length of the signature we are checking in bytes
*
* This just looks at the signed hash... that's why there's no input length
* parameter, it's decided by the choice of hash. It's up to you to confirm
* separately the actual payload matches the hash that was confirmed by this to
* be validly signed.
*
* Returns <0 for error, or 0 if signature matches the hash + key..
*
* The JWS ECDSA signature verification algorithm differs to generic ECDSA
* signatures and they're not interoperable.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
const uint8_t *sig, size_t sig_len);
/** lws_genecdsa_hash_sign_jws() - Creates a JWS ECDSA signature for a hash you provide
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: precomputed hash
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param keybits: number of bits in the crypto key
* \param sig: pointer to buffer to take signature
* \param sig_len: length of the buffer (must be >= length of key N)
*
* Returns <0 for error, or >=0 for success.
*
* This creates a JWS ECDSA signature for a hash you already computed and provide.
*
* The JWS ECDSA signature generation algorithm differs to generic ECDSA
* signatures and they're not interoperable.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
uint8_t *sig, size_t sig_len);
/* Apis that apply to both ECDH and ECDSA */
LWS_VISIBLE LWS_EXTERN void
lws_genec_destroy(struct lws_genec_ctx *ctx);
LWS_VISIBLE LWS_EXTERN void
lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el);
LWS_VISIBLE LWS_EXTERN int
lws_genec_dump(struct lws_gencrypto_keyelem *el);

View File

@ -0,0 +1,193 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup generichash Generic Hash
* ## Generic Hash related functions
*
* Lws provides generic hash / digest accessors that abstract the ones
* provided by whatever tls library you are linking against.
*
* It lets you use the same code if you build against mbedtls or OpenSSL
* for example.
*/
///@{
enum lws_genhash_types {
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHASH_TYPE_MD5,
LWS_GENHASH_TYPE_SHA1,
LWS_GENHASH_TYPE_SHA256,
LWS_GENHASH_TYPE_SHA384,
LWS_GENHASH_TYPE_SHA512,
};
enum lws_genhmac_types {
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA256,
LWS_GENHMAC_TYPE_SHA384,
LWS_GENHMAC_TYPE_SHA512,
};
#define LWS_GENHASH_LARGEST 64
#if defined(LWS_WITH_TLS) && defined(LWS_WITH_GENCRYPTO)
struct lws_genhash_ctx {
uint8_t type;
#if defined(LWS_WITH_MBEDTLS)
union {
mbedtls_md5_context md5;
mbedtls_sha1_context sha1;
mbedtls_sha256_context sha256;
mbedtls_sha512_context sha512; /* 384 also uses this */
const mbedtls_md_info_t *hmac;
} u;
#else
const EVP_MD *evp_type;
EVP_MD_CTX *mdctx;
#endif
};
struct lws_genhmac_ctx {
uint8_t type;
#if defined(LWS_WITH_MBEDTLS)
const mbedtls_md_info_t *hmac;
mbedtls_md_context_t ctx;
#else
const EVP_MD *evp_type;
#if defined(LWS_HAVE_EVP_PKEY_new_raw_private_key)
EVP_MD_CTX *ctx;
EVP_PKEY *key;
#else
#if defined(LWS_HAVE_HMAC_CTX_new)
HMAC_CTX *ctx;
#else
HMAC_CTX ctx;
#endif
#endif
#endif
};
/** lws_genhash_size() - get hash size in bytes
*
* \param type: one of LWS_GENHASH_TYPE_...
*
* Returns number of bytes in this type of hash, if the hash type is unknown, it
* will return 0.
*/
LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
lws_genhash_size(enum lws_genhash_types type);
/** lws_genhmac_size() - get hash size in bytes
*
* \param type: one of LWS_GENHASH_TYPE_...
*
* Returns number of bytes in this type of hmac, if the hmac type is unknown, it
* will return 0.
*/
LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
lws_genhmac_size(enum lws_genhmac_types type);
/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use
*
* \param ctx: your struct lws_genhash_ctx
* \param type: one of LWS_GENHASH_TYPE_...
*
* Initializes the hash context for the type you requested
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type);
/** lws_genhash_update() - digest len bytes of the buffer starting at in
*
* \param ctx: your struct lws_genhash_ctx
* \param in: start of the bytes to digest
* \param len: count of bytes to digest
*
* Updates the state of your hash context to reflect digesting len bytes from in
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len);
/** lws_genhash_destroy() - copy out the result digest and destroy the ctx
*
* \param ctx: your struct lws_genhash_ctx
* \param result: NULL, or where to copy the result hash
*
* Finalizes the hash and copies out the digest. Destroys any allocations such
* that ctx can safely go out of scope after calling this.
*
* NULL result is supported so that you can destroy the ctx cleanly on error
* conditions, where there is no valid result.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result);
/** lws_genhmac_init() - prepare your struct lws_genhmac_ctx for use
*
* \param ctx: your struct lws_genhmac_ctx
* \param type: one of LWS_GENHMAC_TYPE_...
* \param key: pointer to the start of the HMAC key
* \param key_len: length of the HMAC key
*
* Initializes the hash context for the type you requested
*
* If the return is nonzero, it failed and there is nothing needing to be
* destroyed.
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
const uint8_t *key, size_t key_len);
/** lws_genhmac_update() - digest len bytes of the buffer starting at in
*
* \param ctx: your struct lws_genhmac_ctx
* \param in: start of the bytes to digest
* \param len: count of bytes to digest
*
* Updates the state of your hash context to reflect digesting len bytes from in
*
* If the return is nonzero, it failed and needs destroying.
*/
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len);
/** lws_genhmac_destroy() - copy out the result digest and destroy the ctx
*
* \param ctx: your struct lws_genhmac_ctx
* \param result: NULL, or where to copy the result hash
*
* Finalizes the hash and copies out the digest. Destroys any allocations such
* that ctx can safely go out of scope after calling this.
*
* NULL result is supported so that you can destroy the ctx cleanly on error
* conditions, where there is no valid result.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result);
#endif
///@}

View File

@ -0,0 +1,255 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup genericRSA Generic RSA
* ## Generic RSA related functions
*
* Lws provides generic RSA functions that abstract the ones
* provided by whatever OpenSSL library you are linking against.
*
* It lets you use the same code if you build against mbedtls or OpenSSL
* for example.
*/
///@{
/* include/libwebsockets/lws-jwk.h must be included before this */
enum enum_genrsa_mode {
LGRSAM_PKCS1_1_5,
LGRSAM_PKCS1_OAEP_PSS,
LGRSAM_COUNT
};
struct lws_genrsa_ctx {
#if defined(LWS_WITH_MBEDTLS)
mbedtls_rsa_context *ctx;
#else
BIGNUM *bn[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
EVP_PKEY_CTX *ctx;
RSA *rsa;
#endif
struct lws_context *context;
enum enum_genrsa_mode mode;
};
/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
*
* \param ctx: your struct lws_genrsa_ctx
* \param el: struct prepared with key element data
* \param context: lws_context for RNG
* \param mode: RSA mode, one of LGRSAM_ constants
* \param oaep_hashid: the lws genhash id for the hash used in MFG1 hash
* used in OAEP mode - normally, SHA1
*
* Creates an RSA context with a public key associated with it, formed from
* the key elements in \p el.
*
* Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's
* recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations.
*
* Returns 0 for OK or nonzero for error.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_create(struct lws_genrsa_ctx *ctx,
const struct lws_gencrypto_keyelem *el,
struct lws_context *context, enum enum_genrsa_mode mode,
enum lws_genhash_types oaep_hashid);
/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
*
* \param el: your struct lws_gencrypto_keyelem
*
* This is a helper for user code making use of struct lws_gencrypto_keyelem
* where the elements are allocated on the heap, it frees any non-NULL
* buf element and sets the buf to NULL.
*
* NB: lws_genrsa_public_... apis do not need this as they take care of the key
* creation and destruction themselves.
*/
LWS_VISIBLE LWS_EXTERN void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
/** lws_genrsa_new_keypair() - Create new RSA keypair
*
* \param context: your struct lws_context (may be used for RNG)
* \param ctx: your struct lws_genrsa_ctx
* \param mode: RSA mode, one of LGRSAM_ constants
* \param el: struct to get the new key element data allocated into it
* \param bits: key size, eg, 4096
*
* Creates a new RSA context and generates a new keypair into it, with \p bits
* bits.
*
* Returns 0 for OK or nonzero for error.
*
* Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's
* recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el,
int bits);
/** lws_genrsa_public_encrypt() - Perform RSA public key encryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: plaintext input
* \param in_len: length of plaintext input
* \param out: encrypted output
*
* Performs PKCS1 v1.5 Encryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out);
/** lws_genrsa_private_encrypt() - Perform RSA private key encryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: plaintext input
* \param in_len: length of plaintext input
* \param out: encrypted output
*
* Performs PKCS1 v1.5 Encryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out);
/** lws_genrsa_public_decrypt() - Perform RSA public key decryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: encrypted input
* \param in_len: length of encrypted input
* \param out: decrypted output
* \param out_max: size of output buffer
*
* Performs PKCS1 v1.5 Decryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max);
/** lws_genrsa_private_decrypt() - Perform RSA private key decryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: encrypted input
* \param in_len: length of encrypted input
* \param out: decrypted output
* \param out_max: size of output buffer
*
* Performs PKCS1 v1.5 Decryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max);
/** lws_genrsa_hash_sig_verify() - Verifies RSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: input to be hashed
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param sig: pointer to the signature we received with the payload
* \param sig_len: length of the signature we are checking in bytes
*
* Returns <0 for error, or 0 if signature matches the payload + key.
*
* This just looks at a hash... that's why there's no input length
* parameter, it's decided by the choice of hash. It's up to you to confirm
* separately the actual payload matches the hash that was confirmed by this to
* be validly signed.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
const uint8_t *sig, size_t sig_len);
/** lws_genrsa_hash_sign() - Creates an ECDSA signature for a hash you provide
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: input to be hashed and signed
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param sig: pointer to buffer to take signature
* \param sig_len: length of the buffer (must be >= length of key N)
*
* Returns <0 for error, or \p sig_len for success.
*
* This creates an RSA signature for a hash you already computed and provide.
* You should have created the hash before calling this by iterating over the
* actual payload you need to confirm.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
uint8_t *sig, size_t sig_len);
/** lws_genrsa_public_decrypt_destroy() - Destroy RSA public decrypt context
*
* \param ctx: your struct lws_genrsa_ctx
*
* Destroys any allocations related to \p ctx.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN void
lws_genrsa_destroy(struct lws_genrsa_ctx *ctx);
/** lws_genrsa_render_pkey_asn1() - Exports public or private key to ASN1/DER
*
* \param ctx: your struct lws_genrsa_ctx
* \param _private: 0 = public part only, 1 = all parts of the key
* \param pkey_asn1: pointer to buffer to take the ASN1
* \param pkey_asn1_len: max size of the pkey_asn1_len
*
* Returns length of pkey_asn1 written, or -1 for error.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
uint8_t *pkey_asn1, size_t pkey_asn1_len);
///@}

View File

@ -0,0 +1,60 @@
/*
* Generic GPIO ops
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for gpio, a real implementation provides
* functions for the ops that use the underlying OS gpio arrangements.
*/
#if !defined(__LWS_GPIO_H__)
#define __LWS_GPIO_H__
typedef int _lws_plat_gpio_t;
typedef enum {
LWSGGPIO_IRQ_NONE,
LWSGGPIO_IRQ_RISING,
LWSGGPIO_IRQ_FALLING,
LWSGGPIO_IRQ_CHANGE,
LWSGGPIO_IRQ_LOW,
LWSGGPIO_IRQ_HIGH
} lws_gpio_irq_t;
enum {
LWSGGPIO_FL_READ = (1 << 0),
LWSGGPIO_FL_WRITE = (1 << 1),
LWSGGPIO_FL_PULLUP = (1 << 2),
LWSGGPIO_FL_PULLDOWN = (1 << 3),
LWSGGPIO_FL_START_LOW = (1 << 4),
};
typedef void (*lws_gpio_irq_cb_t)(void *arg);
typedef struct lws_gpio_ops {
void (*mode)(_lws_plat_gpio_t gpio, int flags);
int (*read)(_lws_plat_gpio_t gpio);
void (*set)(_lws_plat_gpio_t gpio, int val);
int (*irq_mode)(_lws_plat_gpio_t gpio, lws_gpio_irq_t irq,
lws_gpio_irq_cb_t cb, void *arg);
} lws_gpio_ops_t;
#endif

View File

@ -0,0 +1,717 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Extremely Lightweight HTML5 Stream Parser, same approach as lecp but for
* html5.
*/
#if !defined(LHP_MAX_ELEMS_NEST)
#define LHP_MAX_ELEMS_NEST 32
#endif
#if !defined(LHP_MAX_DEPTH)
#define LHP_MAX_DEPTH 12
#endif
#if !defined(LHP_STRING_CHUNK)
#define LHP_STRING_CHUNK 254
#endif
enum lhp_callbacks {
LHPCB_ERR_ATTRIB_SYNTAX = -5,
LHPCB_ERR_ATTRIB_LEN = -4,
LHPCB_ERR_OOM = -3,
LHPCB_ERR_ELEM_DEPTH = -2,
LHPCB_CONTINUE = -1,
LHPCB_CONSTRUCTED = 0,
LHPCB_DESTRUCTED = 1,
LHPCB_COMPLETE = 2,
LHPCB_FAILED = 3,
LHPCB_ELEMENT_START = 4, /* reported at end of <> */
LHPCB_ELEMENT_END = 5,
LHPCB_CONTENT = 6,
LHPCB_COMMENT = 7,
};
/*
* CSS v2.1 full property set, taken from
*
* https://www.w3.org/TR/CSS21/propidx.html
*/
typedef enum lcsp_props {
LCSP_PROP_AZIMUTH,
LCSP_PROP_BACKGROUND_ATTACHMENT,
LCSP_PROP_BACKGROUND_COLOR,
LCSP_PROP_BACKGROUND_IMAGE,
LCSP_PROP_BACKGROUND_POSITION,
LCSP_PROP_BACKGROUND_REPEAT,
LCSP_PROP_BACKGROUND,
LCSP_PROP_BORDER_COLLAPSE,
LCSP_PROP_BORDER_COLOR,
LCSP_PROP_BORDER_SPACING,
LCSP_PROP_BORDER_STYLE,
LCSP_PROP_BORDER_TOP,
LCSP_PROP_BORDER_RIGHT,
LCSP_PROP_BORDER_BOTTOM,
LCSP_PROP_BORDER_LEFT,
LCSP_PROP_BORDER_TOP_COLOR,
LCSP_PROP_BORDER_RIGHT_COLOR,
LCSP_PROP_BORDER_BOTTOM_COLOR,
LCSP_PROP_BORDER_LEFT_COLOR,
LCSP_PROP_BORDER_TOP_STYLE,
LCSP_PROP_BORDER_RIGHT_STYLE,
LCSP_PROP_BORDER_BOTTOM_STYLE,
LCSP_PROP_BORDER_LEFT_STYLE,
LCSP_PROP_BORDER_TOP_WIDTH,
LCSP_PROP_BORDER_RIGHT_WIDTH,
LCSP_PROP_BORDER_BOTTOM_WIDTH,
LCSP_PROP_BORDER_LEFT_WIDTH,
LCSP_PROP_BORDER_WIDTH,
LCSP_PROP_BORDER_TOP_LEFT_RADIUS,
LCSP_PROP_BORDER_TOP_RIGHT_RADIUS,
LCSP_PROP_BORDER_BOTTOM_LEFT_RADIUS,
LCSP_PROP_BORDER_BOTTOM_RIGHT_RADIUS,
LCSP_PROP_BORDER_RADIUS,
LCSP_PROP_BORDER,
LCSP_PROP_BOTTOM,
LCSP_PROP_CAPTION_SIDE,
LCSP_PROP_CLEAR,
LCSP_PROP_CLIP,
LCSP_PROP_COLOR,
LCSP_PROP_CONTENT,
LCSP_PROP_COUNTER_INCREMENT,
LCSP_PROP_COUNTER_RESET,
LCSP_PROP_CUE_AFTER,
LCSP_PROP_CUE_BEFORE,
LCSP_PROP_CUE,
LCSP_PROP_CURSOR,
LCSP_PROP_DIRECTION,
LCSP_PROP_DISPLAY,
LCSP_PROP_ELEVATION,
LCSP_PROP_EMPTY_CELLS,
LCSP_PROP_FLOAT,
LCSP_PROP_FONT_FAMILY,
LCSP_PROP_FONT_SIZE,
LCSP_PROP_FONT_STYLE,
LCSP_PROP_FONT_VARAIANT,
LCSP_PROP_FONT_WEIGHT,
LCSP_PROP_FONT,
LCSP_PROP_HEIGHT,
LCSP_PROP_LEFT,
LCSP_PROP_LETTER_SPACING,
LCSP_PROP_LINE_HEIGHT,
LCSP_PROP_LIST_STYLE_IMAGE,
LCSP_PROP_LIST_STYLE_POSITION,
LCSP_PROP_LIST_STYLE_TYPE,
LCSP_PROP_LIST_STYLE,
LCSP_PROP_MARGIN_RIGHT,
LCSP_PROP_MARGIN_LEFT,
LCSP_PROP_MARGIN_TOP,
LCSP_PROP_MARGIN_BOTTOM,
LCSP_PROP_MARGIN,
LCSP_PROP_MAX_HEIGHT,
LCSP_PROP_MAX_WIDTH,
LCSP_PROP_MIN_HEIGHT,
LCSP_PROP_MIN_WIDTH,
LCSP_PROP_ORPHANS,
LCSP_PROP_OUTLINE_COLOR,
LCSP_PROP_OUTLINE_STYLE,
LCSP_PROP_OUTLINE_WIDTH,
LCSP_PROP_OUTLINE,
LCSP_PROP_OVERFLOW,
LCSP_PROP_PADDING_TOP,
LCSP_PROP_PADDING_RIGHT,
LCSP_PROP_PADDING_BOTTOM,
LCSP_PROP_PADDING_LEFT,
LCSP_PROP_PADDING,
LCSP_PROP_PAGE_BREAK_AFTER,
LCSP_PROP_PAGE_BREAK_BEFORE,
LCSP_PROP_PAGE_BREAK_INSIDE,
LCSP_PROP_PAUSE_AFTER,
LCSP_PROP_PAUSE_BEFORE,
LCSP_PROP_PAUSE,
LCSP_PROP_PITCH_RANGE,
LCSP_PROP_PITCH,
LCSP_PROP_PLAY_DURING,
LCSP_PROP_POSITION,
LCSP_PROP_QUOTES,
LCSP_PROP_RICHNESS,
LCSP_PROP_RIGHT,
LCSP_PROP_SPEAK_HEADER,
LCSP_PROP_SPEAK_NUMERAL,
LCSP_PROP_SPEAK_PUNCTUATION,
LCSP_PROP_SPEAK,
LCSP_PROP_SPEECH_RATE,
LCSP_PROP_STRESS,
LCSP_PROP_TABLE_LAYOUT,
LCSP_PROP_TEXT_ALIGN,
LCSP_PROP_TEXT_DECORATION,
LCSP_PROP_TEXT_INDENT,
LCSP_PROP_TEXT_TRANSFORM,
LCSP_PROP_TOP,
LCSP_PROP_UNICODE_BIDI,
LCSP_PROP_VERTICAL_ALIGN,
LCSP_PROP_VISIBILITY,
LCSP_PROP_VOICE_FAMILY,
LCSP_PROP_VOLUME,
LCSP_PROP_WHITE_SPACE,
LCSP_PROP_WIDOWS,
LCSP_PROP_WIDTH,
LCSP_PROP_WORD_SPACING,
LCSP_PROP_Z_INDEX,
LCSP_PROP__COUNT /* always last */
} lcsp_props_t;
/*
* Indexes for the well-known property values
*/
typedef enum {
LCSP_PROPVAL_ABOVE,
LCSP_PROPVAL_ABSOLUTE,
LCSP_PROPVAL_ALWAYS,
LCSP_PROPVAL_ARMENIAN,
LCSP_PROPVAL_AUTO,
LCSP_PROPVAL_AVOID,
LCSP_PROPVAL_BASELINE,
LCSP_PROPVAL_BEHIND,
LCSP_PROPVAL_BELOW,
LCSP_PROPVAL_BIDI_OVERRIDE,
LCSP_PROPVAL_BLINK,
LCSP_PROPVAL_BLOCK,
LCSP_PROPVAL_BOLD,
LCSP_PROPVAL_BOLDER,
LCSP_PROPVAL_BOTH,
LCSP_PROPVAL_BOTTOM,
LCSP_PROPVAL_CAPITALIZE,
LCSP_PROPVAL_CAPTION,
LCSP_PROPVAL_CENTER,
LCSP_PROPVAL_CIRCLE,
LCSP_PROPVAL_CLOSE_QUOTE,
LCSP_PROPVAL_CODE,
LCSP_PROPVAL_COLLAPSE,
LCSP_PROPVAL_CONTINUOUS,
LCSP_PROPVAL_CROSSHAIR,
LCSP_PROPVAL_DECIMAL_LEADING_ZERO,
LCSP_PROPVAL_DECIMAL,
LCSP_PROPVAL_DIGITS,
LCSP_PROPVAL_DISC,
LCSP_PROPVAL_EMBED,
LCSP_PROPVAL_E_RESIZE,
LCSP_PROPVAL_FIXED,
LCSP_PROPVAL_GEORGIAN,
LCSP_PROPVAL_HELP,
LCSP_PROPVAL_HIDDEN,
LCSP_PROPVAL_HIDE,
LCSP_PROPVAL_HIGH,
LCSP_PROPVAL_HIGHER,
LCSP_PROPVAL_ICON,
LCSP_PROPVAL_INHERIT,
LCSP_PROPVAL_INLINE,
LCSP_PROPVAL_INLINE_BLOCK,
LCSP_PROPVAL_INLINE_TABLE,
LCSP_PROPVAL_INVERT,
LCSP_PROPVAL_ITALIC,
LCSP_PROPVAL_JUSTIFY,
LCSP_PROPVAL_LEFT,
LCSP_PROPVAL_LIGHTER,
LCSP_PROPVAL_LINE_THROUGH,
LCSP_PROPVAL_LIST_ITEM,
LCSP_PROPVAL_LOW,
LCSP_PROPVAL_LOWER,
LCSP_PROPVAL_LOWER_ALPHA,
LCSP_PROPVAL_LOWERCASE,
LCSP_PROPVAL_LOWER_GREEK,
LCSP_PROPVAL_LOWER_LATIN,
LCSP_PROPVAL_LOWER_ROMAN,
LCSP_PROPVAL_LTR,
LCSP_PROPVAL_MENU,
LCSP_PROPVAL_MESSAGE_BOX,
LCSP_PROPVAL_MIDDLE,
LCSP_PROPVAL_MIX,
LCSP_PROPVAL_MOVE,
LCSP_PROPVAL_NE_RESIZE,
LCSP_PROPVAL_NO_CLOSE_QUOTE,
LCSP_PROPVAL_NONE,
LCSP_PROPVAL_NO_OPEN_QUOTE,
LCSP_PROPVAL_NO_REPEAT,
LCSP_PROPVAL_NORMAL,
LCSP_PROPVAL_NOWRAP,
LCSP_PROPVAL_N_RESIZE,
LCSP_PROPVAL_NW_RESIZE,
LCSP_PROPVAL_OBLIQUE,
LCSP_PROPVAL_ONCE,
LCSP_PROPVAL_OPEN_QUOTE,
LCSP_PROPVAL_OUTSIDE,
LCSP_PROPVAL_OVERLINE,
LCSP_PROPVAL_POINTER,
LCSP_PROPVAL_PRE,
LCSP_PROPVAL_PRE_LINE,
LCSP_PROPVAL_PRE_WRAP,
LCSP_PROPVAL_PROGRESS,
LCSP_PROPVAL_RELATIVE,
LCSP_PROPVAL_REPEAT,
LCSP_PROPVAL_REPEAT_X,
LCSP_PROPVAL_REPEAT_Y,
LCSP_PROPVAL_RIGHT,
LCSP_PROPVAL_RTL,
LCSP_PROPVAL_SCROLL,
LCSP_PROPVAL_SEPARATE,
LCSP_PROPVAL_SE_RESIZE,
LCSP_PROPVAL_SHOW,
LCSP_PROPVAL_SILENT,
LCSP_PROPVAL_SMALL_CAPS,
LCSP_PROPVAL_SMALL_CAPTION,
LCSP_PROPVAL_SPELL_OUT,
LCSP_PROPVAL_SQUARE,
LCSP_PROPVAL_S_RESIZE,
LCSP_PROPVAL_STATIC,
LCSP_PROPVAL_STATUS_BAR,
LCSP_PROPVAL_SUB,
LCSP_PROPVAL_SUPER,
LCSP_PROPVAL_SW_RESIZE,
LCSP_PROPVAL_TABLE,
LCSP_PROPVAL_TABLE_CAPTION,
LCSP_PROPVAL_TABLE_CELL,
LCSP_PROPVAL_TABLE_COLUMN,
LCSP_PROPVAL_TABLE_COLUMN_GROUP,
LCSP_PROPVAL_TABLE_FOOTER_GROUP,
LCSP_PROPVAL_TABLE_HEADER_GROUP,
LCSP_PROPVAL_TABLE_ROW,
LCSP_PROPVAL_TABLE_ROW_GROUP,
LCSP_PROPVAL_TEXT_BOTTOM,
LCSP_PROPVAL_TEXT_TOP,
LCSP_PROPVAL_TEXT,
LCSP_PROPVAL_TOP,
LCSP_PROPVAL_TRANSPARENT,
LCSP_PROPVAL_UNDERLINE,
LCSP_PROPVAL_UPPER_ALPHA,
LCSP_PROPVAL_UPPERCASE,
LCSP_PROPVAL_UPPER_LATIN,
LCSP_PROPVAL_UPPER_ROMAN,
LCSP_PROPVAL_VISIBLE,
LCSP_PROPVAL_WAIT,
LCSP_PROPVAL_W_RESIZE,
LCSP_PROPVAL__COUNT /* always last */
} lcsp_propvals_t;
struct lhp_ctx;
typedef lws_stateful_ret_t (*lhp_callback)(struct lhp_ctx *ctx, char reason);
/* html attribute */
typedef struct lhp_atr {
lws_dll2_t list;
size_t name_len; /* 0 if it is elem tag */
size_t value_len;
/* name+NUL then value+NUL follow */
} lhp_atr_t;
/*
* In order to lay out the table, we have to incrementally adjust all foregoing
* DLOs as newer cells change the situation. So we have to keep track of all
* cell DLOs in a stack of tables until it's all done.
*/
typedef struct {
lws_dll2_t list; /* ps->table_cols */
lws_dll2_owner_t row_dlos; /* lws_dlo_t in column */
lws_fx_t height; /* currently computed row height */
} lhp_table_row_t;
typedef struct {
lws_dll2_t list; /* ps->table_cols */
lws_dll2_owner_t col_dlos; /* lws_dlo_t in column */
lws_fx_t width; /* currently computed column width */
} lhp_table_col_t;
struct lcsp_atr;
#define CCPAS_TOP 0
#define CCPAS_RIGHT 1
#define CCPAS_BOTTOM 2
#define CCPAS_LEFT 3
typedef struct lhp_pstack {
lws_dll2_t list;
void *user; /* private to the stack level */
lhp_callback cb;
/* static: x,y: offset from parent, w,h: surface size of this object */
lws_box_t drt;
/* dynamic cursor inside drt for progressive child placement */
lws_fx_t curx;
lws_fx_t cury;
lws_fx_t widest;
lws_fx_t deepest;
lws_dlo_t *dlo_set_curx;
lws_dlo_t *dlo_set_cury;
lws_dll2_owner_t atr; /* lhp_atr_t */
const lws_display_font_t *f;
const struct lcsp_atr *css_background_color;
const struct lcsp_atr *css_color;
const struct lcsp_atr *css_position;
const struct lcsp_atr *css_display;
const struct lcsp_atr *css_width;
const struct lcsp_atr *css_height;
const struct lcsp_atr *css_border_radius[4];
const struct lcsp_atr *css_pos[4];
const struct lcsp_atr *css_margin[4];
const struct lcsp_atr *css_padding[4];
uint16_t tr_idx; /* in table */
uint16_t td_idx; /* in current tr */
uint8_t is_block:1; /* children use space in our drt */
uint8_t is_table:1;
/* user layout owns these after initial values set */
lws_dlo_t *dlo;
const lws_display_font_t *font;
int oi[4];
int positioned[4];
int rel_layout_cursor[4];
uint8_t runon; /* continues same line */
} lhp_pstack_t;
typedef enum lcsp_css_units {
LCSP_UNIT_NONE,
LCSP_UNIT_NUM, /* u.i */
LCSP_UNIT_LENGTH_EM, /* u.i */
LCSP_UNIT_LENGTH_EX, /* u.i */
LCSP_UNIT_LENGTH_IN, /* u.i */
LCSP_UNIT_LENGTH_CM, /* u.i */
LCSP_UNIT_LENGTH_MM, /* u.i */
LCSP_UNIT_LENGTH_PT, /* u.i */
LCSP_UNIT_LENGTH_PC, /* u.i */
LCSP_UNIT_LENGTH_PX, /* u.i */
LCSP_UNIT_LENGTH_PERCENT, /* u.i */
LCSP_UNIT_ANGLE_ABS_DEG, /* u.i */
LCSP_UNIT_ANGLE_REL_DEG, /* u.i */
LCSP_UNIT_FREQ_HZ, /* u.i */
LCSP_UNIT_RGBA, /* u.rgba */
LCSP_UNIT_URL, /* string at end of atr */
LCSP_UNIT_STRING, /* string at end of atr */
LCSP_UNIT_DATA, /* binary data at end of atr */
} lcsp_css_units_t;
typedef struct lcsp_atr {
lws_dll2_t list;
int propval; /* lcsp_propvals_t LCSP_PROPVAL_ */
size_t value_len; /* for string . url */
lcsp_css_units_t unit;
union {
lws_fx_t i;
uint32_t rgba; /* for colours */
} u;
lws_fx_t r;
uint8_t op;
/* .value_len bytes follow (for strings and blobs) */
} lcsp_atr_t;
/* css definitions like font-weight: */
typedef struct lcsp_defs {
lws_dll2_t list;
lws_dll2_owner_t atrs; /* lcsp_atr_t */
lcsp_props_t prop; /* lcsp_props_t, LCSP_PROP_* */
} lcsp_defs_t;
typedef struct lcsp_names {
lws_dll2_t list;
size_t name_len;
/* name + NUL follow */
} lcsp_names_t;
typedef struct lcsp_stanza { /* css stanza, with names and defs */
lws_dll2_t list;
lws_dll2_owner_t names; /* lcsp_names_t */
lws_dll2_owner_t defs; /* lcsp_defs_t */
} lcsp_stanza_t;
/*
* A list of stanza references can easily have to bring in the same stanza
* multiple times, eg, <div><span class=x><div> won't work unless the div
* stanzas are listed twice at different places in the list. It means we can't
* use dll2 directly since the number of references is open-ended.
*
* lcsp_stanza_ptr provides indirection that allows multiple listings.
*/
typedef struct lcsp_stanza_ptr {
lws_dll2_t list;
lcsp_stanza_t *stz;
} lcsp_stanza_ptr_t;
typedef struct lcsp_atr_ptr {
lws_dll2_t list;
lcsp_atr_t *atr;
} lcsp_atr_ptr_t;
#define LHP_FLAG_DOCUMENT_END (1 << 0)
typedef struct lhp_ctx {
lws_dll2_owner_t stack; /* lhp_pstack_t */
struct lwsac *cssac; /* css allocations all in an ac */
struct lwsac *cascadeac; /* active_stanzas ac */
struct lwsac *propatrac; /* prop atr query results ac */
lws_dll2_owner_t css; /* lcsp_stanza_t (all in ac) */
lws_dll2_owner_t *ids;
lws_fx_t tf;
lcsp_css_units_t unit;
lcsp_stanza_t *stz; /* current stanza getting properties */
lcsp_defs_t *def; /* current property getting values */
lws_dll2_owner_t active_stanzas; /* lcsp_stanza_ptr_t allocated
* in cascadeac */
lws_dll2_owner_t active_atr; /* lcsp_atr_ptr_t allocated in
* propatrac */
lws_surface_info_t ic;
const char *base_url; /* strdup of https://x.com/y.html */
sul_cb_t ssevcb; /* callback for ss events */
lws_sorted_usec_list_t *ssevsul; /* sul to use to resume rz */
sul_cb_t sshtmlevcb; /* callback for more html parse */
lws_sorted_usec_list_t *sshtmlevsul; /* sul for more html parse */
void *user;
void *user1;
const char *tag; /* private */
size_t tag_len; /* private */
int npos;
int state; /* private */
int state_css_comm; /* private */
int nl_temp;
int temp_count;
uint32_t flags;
uint32_t temp;
int32_t window; /* 0, or ss item flow control limit */
union {
uint32_t s;
struct {
uint32_t first:1;
uint32_t closing:1;
uint32_t void_element:1;
uint32_t doctype:1;
uint32_t inq:1;
uint32_t tag_used:1;
uint32_t arg:1;
uint32_t default_css:1;
#define LHP_CSS_PROPVAL_INT_WHOLE 1
#define LHP_CSS_PROPVAL_INT_FRAC 2
#define LHP_CSS_PROPVAL_INT_UNIT 3
uint32_t integer:2;
uint32_t color:2;
} f;
} u;
int prop; /* lcsp_props_t */
int propval; /* lcsp_propvals_t */
int16_t css_state; /* private */
int16_t cssval_state; /* private */
uint8_t in_body:1;
uint8_t finish_css:1;
uint8_t is_css:1;
uint8_t await_css_done:1;
/* at end so we can memset members above it in one go */
char buf[LHP_STRING_CHUNK + 1];
} lhp_ctx_t;
/*
* lws_lhp_construct() - Construct an lhp context
*
* \param ctx: the lhp context to prepare
* \param cb: the stream parsing callback
* \param user: opaque user pointer available from the lhp context
* \param ic: struct with arguments for lhp context
*
* The lhp context is allocated by the caller (the size is known).
* Prepares an lhp context to parse html. Returns 0 for OK, or nonzero if OOM.
*/
LWS_VISIBLE LWS_EXTERN int
lws_lhp_construct(lhp_ctx_t *ctx, lhp_callback cb, void *user,
const lws_surface_info_t *ic);
/*
* lws_lhp_destruct() - Destroy an lhp context
*
* \param ctx: the lhp context to prepare
*
* Destroys an lhp context. The lhp context is allocated by the caller (the
* size is known). But there are suballocations that must be destroyed with
* this.
*/
LWS_VISIBLE LWS_EXTERN void
lws_lhp_destruct(lhp_ctx_t *ctx);
/**
* lws_lhp_ss_browse() - browse url using SS and parse via lhp to DLOs
*
* \param cx: the lws_context
* \param rs: the user's render state object
* \param url: the https://x.com/y.xyz URL to browse
* \param render: the user's linewise render callback (called from \p rs.sul)
*
* High level network fetch via SS and render html via lhp / DLO
*
* rs->ic must be prepared before calling.
*
* Returns nonzero if an early, fatal problem, else returns 0 and continues
* asynchronously.
*
* If rs->box is (0,0,0,0) on entry, it is set to represent the whole display
* surface. Otherwise if not representing the whole display surface, it
* indicates partial mode should be used.
*/
LWS_VISIBLE LWS_EXTERN int
lws_lhp_ss_browse(struct lws_context *cx, lws_display_render_state_t *rs,
const char *url, sul_cb_t render);
/**
* lws_lhp_parse() - parses a chunk of input HTML
*
* \p ctx: the parsing context
* \p buf: pointer to the start of the chunk of html
* \p len: pointer the number of bytes of html available at *\pbuf
*
* Parses up to *len bytes at *buf. On exit, *buf and *len are adjusted
* according to how much data was used. May return before processing all the
* input.
*
* Returns LWS_SRET_WANT_INPUT if the parsing is stalled on some other async
* event (eg, fetch of image to find out the dimensions).
*
* The lws_lhp_ss_browse() api wraps this.
*/
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_lhp_parse(lhp_ctx_t *ctx, const uint8_t **buf, size_t *len);
/**
* lws_css_cascade_get_prop_atr() - create active css atr list for property
*
* \p ctx: the parsing context
* \p prop: the LCSP_PROP_ property to generate the attribute list for
*
* Returns NULL if no atr or OOM.
*
* Otherwise produces a list of active CSS property attributes walkable via
* ctx->active_atr, and returns the tail one. For simple attributes where the
* last definition is the active one, this points to the last definition.
*/
LWS_VISIBLE LWS_EXTERN const lcsp_atr_t *
lws_css_cascade_get_prop_atr(lhp_ctx_t *ctx, lcsp_props_t prop);
/**
* lws_http_rel_to_url() - make absolute url from base and relative
*
* \param dest: place to store the result
* \param len: max length of result including NUL
* \param base: a reference url including a file part
* \param rel: the absolute or relative url or path to apply to base
*
* Copy the url formof rel into dest, using base to fill in missing context
*
* If base is https://x.com/y/z.html
*
* a.html -> https://x.com/y/a/html
* ../b.html -> https://x.com/b.html
* /c.html -> https://x.com/c.html
* https://y.com/a.html -> https://y.com/a.html
*/
LWS_VISIBLE LWS_EXTERN int
lws_http_rel_to_url(char *dest, size_t len, const char *base, const char *rel);
LWS_VISIBLE LWS_EXTERN lhp_pstack_t *
lws_css_get_parent_block(lhp_ctx_t *ctx, lhp_pstack_t *ps);
LWS_VISIBLE LWS_EXTERN const char *
lws_css_pstack_name(lhp_pstack_t *ps);
LWS_VISIBLE LWS_EXTERN const char *
lws_html_get_atr(lhp_pstack_t *ps, const char *aname, size_t aname_len);
LWS_VISIBLE LWS_EXTERN const lws_fx_t *
lws_csp_px(const lcsp_atr_t *a, lhp_pstack_t *ps);
LWS_VISIBLE LWS_EXTERN void
lws_lhp_tag_dlo_id(lhp_ctx_t *ctx, lhp_pstack_t *ps, lws_dlo_t *dlo);
void
lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo);
#define LWS_LHPREF_WIDTH 0
#define LWS_LHPREF_HEIGHT 1
#define LWS_LHPREF_NONE 2
LWS_VISIBLE LWS_EXTERN int
lhp_prop_axis(const lcsp_atr_t *a);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
/*
* Generic I2C ops
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for i2c, a real implementation provides
* functions for the ops that use the underlying OS arrangements.
*/
#if !defined(__LWS_I2C_H__)
#define __LWS_I2C_H__
#include <stdint.h>
#include <stddef.h>
typedef struct lws_i2c_ops {
int (*init)(const struct lws_i2c_ops *ctx);
int (*start)(const struct lws_i2c_ops *ctx);
void (*stop)(const struct lws_i2c_ops *ctx);
int (*write)(const struct lws_i2c_ops *ctx, uint8_t data);
int (*read)(const struct lws_i2c_ops *ctx);
void (*set_ack)(const struct lws_i2c_ops *octx, int ack);
} lws_i2c_ops_t;
/*
* These are implemented by calling the ops above, and so are generic
*/
LWS_VISIBLE LWS_EXTERN int
lws_i2c_command(const lws_i2c_ops_t *ctx, uint8_t ads7, uint8_t c);
LWS_VISIBLE LWS_EXTERN int
lws_i2c_command_list(const lws_i2c_ops_t *ctx, uint8_t ads7, const uint8_t *buf,
size_t len);
#endif

View File

@ -0,0 +1,54 @@
/*
* lws abstract display implementation for ili9341 on spi
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(__LWS_DISPLAY_ILI9341_SPI_H__)
#define __LWS_DISPLAY_ILI9341_SPI_H__
typedef struct lws_display_ili9341 {
lws_display_t disp; /* use lws_display_ili9341_ops to set */
const lws_spi_ops_t *spi; /* spi ops */
lws_display_completion_t cb;
const lws_gpio_ops_t *gpio; /* NULL or gpio ops */
_lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */
uint8_t spi_index; /* cs index starting from 0 */
} lws_display_ili9341_t;
int
lws_display_ili9341_spi_init(lws_display_state_t *lds);
int
lws_display_ili9341_spi_blit(lws_display_state_t *lds, const uint8_t *src,
lws_box_t *box, lws_dll2_owner_t *ids);
int
lws_display_ili9341_spi_power(lws_display_state_t *lds, int state);
#define lws_display_ili9341_ops \
.init = lws_display_ili9341_spi_init, \
.blit = lws_display_ili9341_spi_blit, \
.power = lws_display_ili9341_spi_power
#endif

View File

@ -0,0 +1,215 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
enum lws_jws_jose_hdr_indexes {
LJJHI_ALG, /* REQUIRED */
LJJHI_JKU, /* Optional: string */
LJJHI_JWK, /* Optional: jwk JSON object: public key: */
LJJHI_KID, /* Optional: string */
LJJHI_X5U, /* Optional: string: url of public key cert / chain */
LJJHI_X5C, /* Optional: base64 (NOT -url): actual cert */
LJJHI_X5T, /* Optional: base64url: SHA-1 of actual cert */
LJJHI_X5T_S256, /* Optional: base64url: SHA-256 of actual cert */
LJJHI_TYP, /* Optional: string: media type */
LJJHI_CTY, /* Optional: string: content media type */
LJJHI_CRIT, /* Optional for send, REQUIRED: array of strings:
* mustn't contain standardized strings or null set */
LJJHI_RECIPS_HDR,
LJJHI_RECIPS_HDR_ALG,
LJJHI_RECIPS_HDR_KID,
LJJHI_RECIPS_EKEY,
LJJHI_ENC, /* JWE only: Optional: string */
LJJHI_ZIP, /* JWE only: Optional: string ("DEF" = deflate) */
LJJHI_EPK, /* Additional arg for JWE ECDH: ephemeral public key */
LJJHI_APU, /* Additional arg for JWE ECDH: base64url */
LJJHI_APV, /* Additional arg for JWE ECDH: base64url */
LJJHI_IV, /* Additional arg for JWE AES: base64url */
LJJHI_TAG, /* Additional arg for JWE AES: base64url */
LJJHI_P2S, /* Additional arg for JWE PBES2: base64url: salt */
LJJHI_P2C, /* Additional arg for JWE PBES2: integer: count */
LWS_COUNT_JOSE_HDR_ELEMENTS
};
enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_CBC,
LWS_JOSE_ENCTYPE_AES_CFB128,
LWS_JOSE_ENCTYPE_AES_CFB8,
LWS_JOSE_ENCTYPE_AES_CTR,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_AES_OFB,
LWS_JOSE_ENCTYPE_AES_XTS, /* care: requires double-length key */
LWS_JOSE_ENCTYPE_AES_GCM,
};
/* there's a table of these defined in lws-gencrypto-common.c */
struct lws_jose_jwe_alg {
enum lws_genhash_types hash_type;
enum lws_genhmac_types hmac_type;
enum lws_jose_algtype algtype_signing; /* the signing cipher */
enum lws_jose_algtype algtype_crypto; /* the encryption cipher */
const char *alg; /* the JWA enc alg name, eg "ES512" */
const char *curve_name; /* NULL, or, eg, "P-256" */
unsigned short keybits_min, keybits_fixed;
unsigned short ivbits;
};
/*
* For JWS, "JOSE header" is defined to be the union of...
*
* o JWS Protected Header
* o JWS Unprotected Header
*
* For JWE, the "JOSE header" is the union of...
*
* o JWE Protected Header
* o JWE Shared Unprotected Header
* o JWE Per-Recipient Unprotected Header
*/
#define LWS_JWS_MAX_RECIPIENTS 3
struct lws_jws_recpient {
/*
* JOSE per-recipient unprotected header... for JWS this contains
* protected / header / signature
*/
struct lws_gencrypto_keyelem unprot[LWS_COUNT_JOSE_HDR_ELEMENTS];
struct lws_jwk jwk_ephemeral; /* recipient ephemeral key if any */
struct lws_jwk jwk; /* recipient "jwk" key if any */
};
struct lws_jose {
/* JOSE protected and unprotected header elements */
struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
struct lws_jws_recpient recipient[LWS_JWS_MAX_RECIPIENTS];
char typ[32];
char edone[LWS_COUNT_JOSE_HDR_ELEMENTS];
/* information from the protected header part */
const struct lws_jose_jwe_alg *alg;
const struct lws_jose_jwe_alg *enc_alg;
int recipients; /* count of used recipient[] entries */
};
/**
* lws_jose_init() - prepare a struct lws_jose for use
*
* \param jose: the jose header struct to prepare
*/
LWS_VISIBLE LWS_EXTERN void
lws_jose_init(struct lws_jose *jose);
/**
* lws_jose_destroy() - retire a struct lws_jose from use
*
* \param jose: the jose header struct to destroy
*/
LWS_VISIBLE LWS_EXTERN void
lws_jose_destroy(struct lws_jose *jose);
/**
* lws_gencrypto_jws_alg_to_definition() - look up a jws alg name
*
* \param alg: the jws alg name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jws_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
/**
* lws_gencrypto_jwe_alg_to_definition() - look up a jwe alg name
*
* \param alg: the jwe alg name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
/**
* lws_gencrypto_jwe_enc_to_definition() - look up a jwe enc name
*
* \param alg: the jwe enc name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_enc_to_definition(const char *enc,
const struct lws_jose_jwe_alg **jose);
/**
* lws_jws_parse_jose() - parse a JWS JOSE header
*
* \param jose: the jose struct to set to parsing results
* \param buf: the raw JOSE header
* \param len: the length of the raw JOSE header
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* returns 0 for success, or -1 for error
* *\p temp_len is updated to reflect the amount of \p temp used if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len);
/**
* lws_jwe_parse_jose() - parse a JWE JOSE header
*
* \param jose: the jose struct to set to parsing results
* \param buf: the raw JOSE header
* \param len: the length of the raw JOSE header
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* returns 0 for success, or -1 for error
* *\p temp_len is updated to reflect the amount of \p temp used if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len);

View File

@ -0,0 +1,104 @@
/*
* lws jpeg
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Based on public domain original with notice -->
*
* picojpeg.c v1.1 - Public domain, Rich Geldreich <richgel99@gmail.com>
* Nov. 27, 2010 - Initial release
* Feb. 9, 2013 - Added H1V2/H2V1 support, cleaned up macros, signed shift fixes
* Also integrated and tested changes from Chris Phoenix <cphoenix@gmail.com>.
*
* This version is rewritten for lws, changing the whole approach to decode on
* demand to issue a line of output at a time, statefully. This version is
* licensed MIT to match the rest of lws.
*/
typedef struct lws_jpeg lws_jpeg_t;
/**
* lws_jpeg_new() - Create new JPEG decode object
*
* Returns a new jpeg decoding object, which should be destroyed with
* lws_jpeg_free() when done with, or NULL if OOM.
*/
LWS_VISIBLE LWS_EXTERN lws_jpeg_t *
lws_jpeg_new(void);
/**
* lws_jpeg_free() - Destroy a JPEG decode object
*
* \param j: Pointer to the decode object to destroy and set to NULL
*
* This also frees any sub-allocations in the object.
*/
LWS_VISIBLE LWS_EXTERN void
lws_jpeg_free(lws_jpeg_t **j);
/**
* lws_jpeg_emit_next_line() - deocde the next line
*
* \param j: the decode object
* \param ppix: pointer to a pointer set to the line's decoded pixel data
* \param buf: pointer to a const uint8_t array of jpeg input
* \param size: pointer to the count of bytes available at *buf
* \param hold_at_metadata: true if we should not advance to decode
*
* Make jpeg input available to the decoder so it can issue the next line's
* worth of pixels. If the call consumed any input, *buf and *size are
* adjusted accordingly.
*
* The decoder is stateful so it is not sensitive to the chunk size for the
* input.
*
* If \p hold_at_metadata is set, then the decoder will only go as far as
* picking out the metadata like image dimensions, but not start the decode,
* which requires the >30KB heap allocation. This lets you put off for as long
* as possible committing to the decode allocation... this only helps overall
* if you have flow controlled the incoming PNG data.
*
* Return will be one of LWS_SRET_WANT_INPUT is the decoder is stalled waiting
* for more input to be provided, LWS_SRET_WANT_OUTPUT is the decoder stopped
* because it had produced a whole line of output pixels (which can be found
* starting at *ppix), LWS_SRET_OK is it completed and LWS_SRET_FATAL or larger
* if the decode failed.
*
* The output at *ppix is either 3-byte per pixel RGB, or 1-byte grayscale, you
* can query lws_jpeg_get_components() to find out how many bytes per pixel.
*/
LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t
lws_jpeg_emit_next_line(lws_jpeg_t *j, const uint8_t **ppix,
const uint8_t **buf, size_t *size, char hold_at_metadata);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_width(const lws_jpeg_t *j);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_height(const lws_jpeg_t *j);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_bpp(const lws_jpeg_t *j);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_bitdepth(const lws_jpeg_t *j);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_components(const lws_jpeg_t *j);
LWS_VISIBLE LWS_EXTERN unsigned int
lws_jpeg_get_pixelsize(const lws_jpeg_t *j);

View File

@ -0,0 +1,229 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*
* This is a JSON-RPC parser and state management implementation that's:
*
* - Lightweight, it uses lws LEJP JSON stream parser for requests, responses,
* and user-defined parameter objects
*
* - Stateful... you can give it sequential input buffers randomly fragmented
* and it will complete when it has enough
*
* - Asynchronous... response processing can return to the event loop both
* while the RX is still coming and after it's all received before forming
* the response, eg, because it's querying on a remote connection to get the
* response data. Any number of RPCs can be either in flight or waiting for
* response processing to complete before responding.
*
* - Supports "version" extension
*
* - allows binding different method names to different callbacks
*
* - Supports both client and server roles, eg, can parse both requests and
* responses
*
* - No support for batch. Batching is not widely used because it doesn't
* add anything for the vast bulk of cases compared to sending n requests.
*
* This handles client and server RX and transaction state, creating a callback
* when parameters can be parsed and all of the request or notification is
* done.
*
* Producing JSON is usually simpler and more compact than expressing it as an
* object model, ie often a response can be completely formed in a single
* lws_snprintf(). Response JSON must be buffered on heap until the method
* callback is called with NULL / 0 buf len indicating that the incoming request
* has completed parsing.
*
*/
/* these are opaque */
struct lws_jrpc_obj;
struct lws_jrpc;
typedef enum {
LJRPC_CBRET_CONTINUE,
LJRPC_CBRET_WANT_TO_EMIT,
LJRPC_CBRET_FINISHED,
LJRPC_CBRET_FAILED
} lws_jrpc_cb_return_t;
/*
* method name to lejp parsing handler map
*/
typedef struct lws_jrpc_method {
const char *method_name;
const char * const *paths;
lejp_callback cb;
int count_paths;
} lws_jrpc_method_t;
/*
* Boilerplate for forming correct requests
*/
/* Boilerplate to start a request */
#define LWSJRPCBP_REQ_START_S "{\"jsonrpc\":\"2.0\",\"method\":\"%s\""
/* Boilerplate to start parameters (params are left freeform for user) */
#define LWSJRPCBP_REQ_VERSION_S ",\"version\":\"%s\""
/* Boilerplate to start parameters (params are left freeform for user) */
#define LWSJRPCBP_REQ_PARAMS ",\"params\":"
/* Boilerplate to complete the result object */
#define LWSJRPCBP_REQ_NOTIF_END "}"
/* Boilerplate to complete the result object */
#define LWSJRPCBP_REQ_ID_END_S ",\"id\":%s}"
/*
* Boilerplate for forming correct responses
*/
/* Boilerplate to start a result */
#define LWSJRPCBP_RESP_RESULT "{\"jsonrpc\":\"2.0\",\"result\":"
/* Boilerplate to complete the result object */
#define LWSJRPCBP_RESP_ID_END_S ",\"id\":%s}"
/* Boilerplate to form an error */
#define LWSJRPCBP_RESP_ERROR_D "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d"
/* optional */
#define LWSJRPCBP_RESP_ERROR_MSG_S ",\"message\":\"%s\""
/* optional */
#define LWSJRPCBP_RESP_ERROR_DATA ",\"data\":"
/* required */
#define LWSJRPCBP_RESP_ERROR_END "}"
/*
* JSONRPC Well-known Errors
*/
enum {
LWSJRPCE__NO_ERROR = 0,
LWSJRPCWKE__PARSE_ERROR = -32700, /* invalid JSON */
LWSJRPCWKE__INVALID_REQUEST = -32600, /* not valid JSONRPC object */
LWSJRPCWKE__METHOD_NOT_FOUND = -32601, /* method not supported */
LWSJRPCWKE__INVALID_PARAMS = -32602, /* parameters are invalid */
LWSJRPCWKE__INTERNAL_ERROR = -32603, /* internal JSONRPC error */
LWSJRPCWKE__SERVER_ERROR_FIRST = -32000, /* implementation-defined...*/
LWSJRPCWKE__SERVER_ERROR_LAST = -32099, /* ... server errors range */
LWSJRPCE__INVALID_MEMBERS = -31000, /* reponse membs in req, vv */
};
enum {
LWSJRPC_PARSE_REQUEST,
LWSJRPC_PARSE_RESPONSE
};
/*
* APIs for the opaque JRPC request object
*/
/**
* lws_jrpc_obj_parse() - parse a request or response
*
* \param jrpc: the jrpc context this belongs to
* \param type: LWSJRPC_PARSE_REQUEST or ..._RESPONSE
* \param opaque: user-defined pointer bound to lws_jrpc, ignored by lws
* \param buf: chunk of JSON-RPC
* \param l: remaining length of JSON (may be under or oversize)
* \param r: NULL to indicate starting new req, already set means continue parse
*
* If necessary creates an opaque req object and starts parsing len bytes of
* buf. This may be undersize (more parts coming) in which case \p req will be
* set on entry next time indicating a continuation.
*
* \p type and \p opaque are ignored if it it's not the first buffer that
* creates the req object.
*
* Return code is >= 0 if completed, representing the amount of unused data in
* the input buffer. -1 indicates more input data needed, <-1 indicates an
* error from the LWSJRPCWKE_ set above, or LEJP_REJECT_UNKNOWN for OOM
*/
LWS_VISIBLE LWS_EXTERN int
lws_jrpc_obj_parse(struct lws_jrpc *jrpc, int type, void *opaque,
const char *buf, size_t l, struct lws_jrpc_obj **r);
/*
* lws_jrpc_obj_destroy() - detach and destroy a JRPC request or response
*
* \param _r: pointer to pointer to JRPC request to detach and free
*
* Detaches the req from its JRPC context and frees it and any internal
* allocations.
*/
LWS_VISIBLE LWS_EXTERN void
lws_jrpc_obj_destroy(struct lws_jrpc_obj **_r);
/*
* lws_jrpc_obj_get_opaque() - retreive the opaque pointer bound to the req
*
* \param r: pointer to pointer to JRPC request
*
* Returns the opaque pointer for a req given when it was parsed / created.
*/
LWS_VISIBLE LWS_EXTERN void *
lws_jrpc_obj_get_opaque(const struct lws_jrpc_obj *r);
/*
* lws_jrpc_obj_id() - retreive the object's id string
*
* \param r: pointer to pointer to JRPC object
*
* Returns a pointer to a correctly-typed id for use in a response; if a string,
* then it is already quoted, if an int or null then it's provided without
* quotes.
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_jrpc_obj_id(const struct lws_jrpc_obj *r);
/*
* APIs for the opaque JRPC context
*/
/**
* lws_jrpc_create() - Allocate and initialize a JRPC context
*
* \param methods: the method callbacks and names we can process
* \param opaque: user-defined pointer bound to lws_jrpc ignored by lws
*
* Allocates an opaque lws_jrpc object and binds it to the given array of
* method names and callbacks
*/
LWS_VISIBLE LWS_EXTERN struct lws_jrpc *
lws_jrpc_create(const lws_jrpc_method_t *methods, void *opaque);
/*
* lws_jrpc_destroy() - destroy an allocated JRPC context
*
* \param jrpc: pointer to pointer to jrpc to destroy
*
* Destroys any ongoing reqs in the JRPC and then destroys the JRPC and sets the
* given pointer to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_jrpc_destroy(struct lws_jrpc **jrpc);

View File

@ -0,0 +1,164 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*/
#define LWS_JWE_RFC3394_OVERHEAD_BYTES 8
#define LWS_JWE_AES_IV_BYTES 16
#define LWS_JWE_LIMIT_RSA_KEY_BITS 4096
#define LWS_JWE_LIMIT_AES_KEY_BITS (512 + 64) /* RFC3394 Key Wrap adds 64b */
#define LWS_JWE_LIMIT_EC_KEY_BITS 528 /* 521 rounded to byte boundary */
#define LWS_JWE_LIMIT_HASH_BITS (LWS_GENHASH_LARGEST * 8)
/* the largest key element for any cipher */
#define LWS_JWE_LIMIT_KEY_ELEMENT_BYTES (LWS_JWE_LIMIT_RSA_KEY_BITS / 8)
struct lws_jwe {
struct lws_jose jose;
struct lws_jws jws;
struct lws_jwk jwk;
/*
* We have to keep a copy of the CEK so we can reuse it with later
* key encryptions for the multiple recipient case.
*/
uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
unsigned int cek_valid:1;
int recip;
};
LWS_VISIBLE LWS_EXTERN void
lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context);
LWS_VISIBLE LWS_EXTERN void
lws_jwe_destroy(struct lws_jwe *jwe);
LWS_VISIBLE LWS_EXTERN void
lws_jwe_be64(uint64_t c, uint8_t *p8);
/*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len);
LWS_VISIBLE int
lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len);
LWS_VISIBLE LWS_EXTERN int
lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len,
char *temp, int *temp_len);
/**
* lws_jwe_auth_and_decrypt() - confirm and decrypt JWE
*
* \param jose: jose context
* \param jws: jws / jwe context... .map and .map_b64 must be filled already
*
* This is a high level JWE decrypt api that takes a jws with the maps
* already processed, and if the authentication passes, returns the decrypted
* plaintext in jws.map.buf[LJWE_CTXT] and its length in jws.map.len[LJWE_CTXT].
*
* In the jws, the following fields must have been set by the caller
*
* .context
* .jwk (the key encryption key)
* .map
* .map_b64
*
* Having the b64 and decoded maps filled externally makes it flexible where
* the data was picked from, eg, from a Complete JWE JSON serialization, a
* flattened one, or a Compact Serialization.
*
* Returns decrypt length, or -1 for failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len);
/**
* lws_jwe_encrypt() - perform JWE encryption
*
* \param jose: the JOSE header information (encryption types, etc)
* \param jws: the JWE elements, pointer to jwk etc
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* May be called up to LWS_JWS_MAX_RECIPIENTS times to encrypt the same CEK
* multiple ways on the same JWE payload.
*
* returns the amount of temp used, or -1 for error.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_encrypt(struct lws_jwe *jwe, char *temp, int *temp_len);
/**
* lws_jwe_create_packet() - add b64 sig to b64 hdr + payload
*
* \param jwe: the struct lws_jwe we are trying to render
* \param payload: unencoded payload JSON
* \param len: length of unencoded payload JSON
* \param nonce: Nonse string to include in protected header
* \param out: buffer to take signed packet
* \param out_len: size of \p out buffer
* \param conext: lws_context to get random from
*
* This creates a "flattened" JWS packet from the jwk and the plaintext
* payload, and signs it. The packet is written into \p out.
*
* This does the whole packet assembly and signing, calling through to
* lws_jws_sign_from_b64() as part of the process.
*
* Returns the length written to \p out, or -1.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_create_packet(struct lws_jwe *jwe,
const char *payload, size_t len, const char *nonce,
char *out, size_t out_len, struct lws_context *context);
/* only exposed because we have test vectors that need it */
LWS_VISIBLE LWS_EXTERN int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
/* only exposed because we have test vectors that need it */
LWS_VISIBLE LWS_EXTERN int
lws_jwa_concat_kdf(struct lws_jwe *jwe, int direct,
uint8_t *out, const uint8_t *shared_secret, int sslen);

View File

@ -0,0 +1,220 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup jwk JSON Web Keys
* ## JSON Web Keys API
*
* Lws provides an API to parse JSON Web Keys into a struct lws_gencrypto_keyelem.
*
* "oct" and "RSA" type keys are supported. For "oct" keys, they are held in
* the "e" member of the struct lws_gencrypto_keyelem.
*
* Keys elements are allocated on the heap. You must destroy the allocations
* in the struct lws_gencrypto_keyelem by calling
* lws_genrsa_destroy_elements() when you are finished with it.
*/
///@{
enum enum_jwk_meta_tok {
JWK_META_KTY,
JWK_META_KID,
JWK_META_USE,
JWK_META_KEY_OPS,
JWK_META_X5C,
JWK_META_ALG,
LWS_COUNT_JWK_ELEMENTS
};
struct lws_jwk {
/* key data elements */
struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT];
/* generic meta key elements, like KID */
struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS];
int kty; /**< one of LWS_GENCRYPTO_KTY_ */
char private_key; /* nonzero = has private key elements */
};
typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
struct lws_jwk_parse_state {
struct lws_jwk *jwk;
char b64[(((8192 / 8) * 4) / 3) + 1]; /* enough for 8Kb key */
lws_jwk_key_import_callback per_key_cb;
void *user;
int pos;
int cose_state;
int seen;
unsigned short possible;
};
/** lws_jwk_import() - Create a JSON Web key from the textual representation
*
* \param jwk: the JWK object to create
* \param cb: callback for each jwk-processed key, or NULL if importing a single
* key with no parent "keys" JSON
* \param user: pointer to be passed to the callback, otherwise ignored by lws.
* NULL if importing a single key with no parent "keys" JSON
* \param in: a single JWK JSON stanza in utf-8
* \param len: the length of the JWK JSON stanza in bytes
*
* Creates an lws_jwk struct filled with data from the JSON representation.
*
* There are two ways to use this... with some protocols a single jwk is
* delivered with no parent "keys": [] array. If you call this with cb and
* user as NULL, then the input will be interpreted like that and the results
* placed in s.
*
* The second case is that you are dealing with a "keys":[] array with one or
* more keys in it. In this case, the function iterates through the keys using
* s as a temporary jwk, and calls the user-provided callback for each key in
* turn while it return 0 (nonzero return from the callback terminates the
* iteration through any further keys).
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len);
/** lws_jwk_destroy() - Destroy a JSON Web key
*
* \param jwk: the JWK object to destroy
*
* All allocations in the lws_jwk are destroyed
*/
LWS_VISIBLE LWS_EXTERN void
lws_jwk_destroy(struct lws_jwk *jwk);
/** lws_jwk_dup_oct() - Set a jwk to a dup'd binary OCT key
*
* \param jwk: the JWK object to set
* \param key: the JWK object to destroy
* \param len: the JWK object to destroy
*
* Sets the kty to OCT, allocates len bytes for K and copies len bytes of key
* into the allocation.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len);
#define LWSJWKF_EXPORT_PRIVATE (1 << 0)
#define LWSJWKF_EXPORT_NOCRLF (1 << 1)
/** lws_jwk_export() - Export a JSON Web key to a textual representation
*
* \param jwk: the JWK object to export
* \param flags: control export options
* \param p: the buffer to write the exported JWK to
* \param len: the length of the buffer \p p in bytes... reduced by used amount
*
* Returns length of the used part of the buffer if OK, or -1 for error.
*
* \p flags can be OR-ed together
*
* LWSJWKF_EXPORT_PRIVATE: default is only public part, set this to also export
* the private part
*
* LWSJWKF_EXPORT_NOCRLF: normally adds a CRLF at the end of the export, if
* you need to suppress it, set this flag
*
* Serializes the content of the JWK into a char buffer.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len);
/** lws_jwk_load() - Import a JSON Web key from a file
*
* \param jwk: the JWK object to load into
* \param filename: filename to load from
* \param cb: optional callback for each key
* \param user: opaque user pointer passed to cb if given
*
* Returns 0 for OK or -1 for failure
*
* There are two ways to use this... with some protocols a single jwk is
* delivered with no parent "keys": [] array. If you call this with cb and
* user as NULL, then the input will be interpreted like that and the results
* placed in s.
*
* The second case is that you are dealing with a "keys":[] array with one or
* more keys in it. In this case, the function iterates through the keys using
* s as a temporary jwk, and calls the user-provided callback for each key in
* turn while it return 0 (nonzero return from the callback terminates the
* iteration through any further keys, leaving the last one in s).
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user);
/** lws_jwk_save() - Export a JSON Web key to a file
*
* \param jwk: the JWK object to save from
* \param filename: filename to save to
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_save(struct lws_jwk *jwk, const char *filename);
/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint
*
* \param jwk: the JWK object to fingerprint
* \param digest32: buffer to take 32-byte digest
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32);
/** lws_jwk_strdup_meta() - allocate a duplicated string meta element
*
* \param jwk: the JWK object to fingerprint
* \param idx: JWK_META_ element index
* \param in: string to copy
* \param len: length of string to copy
*
* Returns 0 for OK or nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
const char *in, int len);
LWS_VISIBLE LWS_EXTERN int
lws_jwk_dump(struct lws_jwk *jwk);
/** lws_jwk_generate() - create a new key of given type and characteristics
*
* \param context: the struct lws_context used for RNG
* \param jwk: the JWK object to fingerprint
* \param kty: One of the LWS_GENCRYPTO_KTY_ key types
* \param bits: for OCT and RSA keys, the number of bits
* \param curve: for EC keys, the name of the curve
*
* Returns 0 for OK or nonzero for failure
*/
LWS_VISIBLE int
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
enum lws_gencrypto_kty kty, int bits, const char *curve);
///@}

View File

@ -0,0 +1,601 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*! \defgroup jws JSON Web Signature
* ## JSON Web Signature API
*
* Lws provides an API to check and create RFC7515 JSON Web Signatures
*
* SHA256/384/512 HMAC, and RSA 256/384/512 are supported.
*
* The API uses your TLS library crypto, but works exactly the same no matter
* what your TLS backend is.
*/
///@{
/*
* The maps are built to work with both JWS (LJWS_) and JWE (LJWE_), and are
* sized to the slightly larger JWE case.
*/
enum enum_jws_sig_elements {
/* JWS block namespace */
LJWS_JOSE,
LJWS_PYLD,
LJWS_SIG,
LJWS_UHDR,
/* JWE block namespace */
LJWE_JOSE = 0,
LJWE_EKEY,
LJWE_IV,
LJWE_CTXT,
LJWE_ATAG,
LJWE_AAD,
LWS_JWS_MAX_COMPACT_BLOCKS
};
struct lws_jws_map {
const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS];
uint32_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
};
#define LWS_JWS_MAX_SIGS 3
struct lws_jws {
struct lws_jwk *jwk; /* the struct lws_jwk containing the signing key */
struct lws_context *context; /* the lws context (used to get random) */
struct lws_jws_map map, map_b64;
};
/* jws EC signatures do not have ASN.1 in them, meaning they're incompatible
* with generic signatures.
*/
/**
* lws_jws_init() - initialize a jws for use
*
* \param jws: pointer to the jws to initialize
* \param jwk: the jwk to use with this jws
* \param context: the lws_context to use
*/
LWS_VISIBLE LWS_EXTERN void
lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
struct lws_context *context);
/**
* lws_jws_destroy() - scrub a jws
*
* \param jws: pointer to the jws to destroy
*
* Call before the jws goes out of scope.
*
* Elements defined in the jws are zeroed.
*/
LWS_VISIBLE LWS_EXTERN void
lws_jws_destroy(struct lws_jws *jws);
/**
* lws_jws_sig_confirm_compact() - check signature
*
* \param map: pointers and lengths for each of the unencoded JWS elements
* \param jwk: public key
* \param context: lws_context
* \param temp: scratchpad
* \param temp_len: length of scratchpad
*
* Confirms the signature on a JWS. Use if you have non-b64 plain JWS elements
* in a map... it'll make a temp b64 version needed for comparison. See below
* for other variants.
*
* Returns 0 on match, else nonzero.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_compact(struct lws_jws_map *map, struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_compact_b64_map(struct lws_jws_map *map_b64,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
/**
* lws_jws_sig_confirm_compact_b64() - check signature on b64 compact JWS
*
* \param in: pointer to b64 jose.payload[.hdr].sig
* \param len: bytes available at \p in
* \param map: map to take decoded non-b64 content
* \param jwk: public key
* \param context: lws_context
* \param temp: scratchpad
* \param temp_len: size of scratchpad
*
* Confirms the signature on a JWS. Use if you have you have b64 compact layout
* (jose.payload.hdr.sig) as an aggregated string... it'll make a temp plain
* version needed for comparison.
*
* Returns 0 on match, else nonzero.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
struct lws_jws_map *map,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
/**
* lws_jws_sig_confirm() - check signature on plain + b64 JWS elements
*
* \param map_b64: pointers and lengths for each of the b64-encoded JWS elements
* \param map: pointers and lengths for each of the unencoded JWS elements
* \param jwk: public key
* \param context: lws_context
*
* Confirms the signature on a JWS. Use if you have you already have both b64
* compact layout (jose.payload.hdr.sig) and decoded JWS elements in maps.
*
* If you had the b64 string and called lws_jws_compact_decode() on it, you
* will end up with both maps, and can use this api version, saving needlessly
* regenerating any temp map.
*
* Returns 0 on match, else nonzero.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm(struct lws_jws_map *map_b64, /* b64-encoded */
struct lws_jws_map *map, /* non-b64 */
struct lws_jwk *jwk, struct lws_context *context);
/**
* lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
*
* \param jose: jose header information
* \param jws: information to include in the signature
* \param b64_sig: output buffer for b64 signature
* \param sig_len: size of \p b64_sig output buffer
*
* This adds a b64-coded JWS signature of the b64-encoded protected header
* and b64-encoded payload, at \p b64_sig. The signature will be as large
* as the N element of the RSA key when the RSA key is used, eg, 512 bytes for
* a 4096-bit key, and then b64-encoding on top.
*
* In some special cases, there is only payload to sign and no header, in that
* case \p b64_hdr may be NULL, and only the payload will be hashed before
* signing.
*
* If successful, returns the length of the encoded signature written to
* \p b64_sig. If the jose signing type is unknown, 0 is returned. Otherwise
* -1 indicates failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, char *b64_sig,
size_t sig_len);
/**
* lws_jws_compact_decode() - converts and maps compact serialization b64 sections
*
* \param in: the incoming compact serialized b64
* \param len: the length of the incoming compact serialized b64
* \param map: pointer to the results structure
* \param map_b64: NULL, or pointer to a second results structure taking block
* information about the undecoded b64
* \param out: buffer to hold decoded results
* \param out_len: size of out in bytes
*
* Returns number of sections (2 if "none", else 3), or -1 if illegal.
*
* map is set to point to the start and hold the length of each decoded block.
* If map_b64 is non-NULL, then it's set with information about the input b64
* blocks.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_compact_decode(const char *in, int len, struct lws_jws_map *map,
struct lws_jws_map *map_b64, char *out, int *out_len);
LWS_VISIBLE LWS_EXTERN int
lws_jws_compact_encode(struct lws_jws_map *map_b64, /* b64-encoded */
const struct lws_jws_map *map, /* non-b64 */
char *buf, int *out_len);
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_json(const char *in, size_t len,
struct lws_jws *jws, struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
/**
* lws_jws_write_flattened_json() - create flattened JSON sig
*
* \param jws: information to include in the signature
* \param flattened: output buffer for JSON
* \param len: size of \p flattened output buffer
*
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len);
/**
* lws_jws_write_compact() - create flattened JSON sig
*
* \param jws: information to include in the signature
* \param compact: output buffer for compact format
* \param len: size of \p flattened output buffer
*
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len);
/*
* below apis are not normally needed if dealing with whole JWS... they're
* useful for creating from scratch
*/
/**
* lws_jws_dup_element() - allocate space for an element and copy data into it
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param in: data to duplicate into element
* \param in_len: length of data to duplicate
* \param actual_alloc: 0 for same as in_len, else actual allocation size
*
* Copies in_len from in to temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... in_len will be copied into it.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_dup_element(struct lws_jws_map *map, int idx,
char *temp, int *temp_len, const void *in, size_t in_len,
size_t actual_alloc);
/**
* lws_jws_randomize_element() - create an element and fill with random
*
* \param context: lws_context used for random
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param random_len: length of data to fill with random
* \param actual_alloc: 0 for same as random_len, else actual allocation size
*
* Randomize random_len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the first random_len will be filled with random.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_randomize_element(struct lws_context *context,
struct lws_jws_map *map,
int idx, char *temp, int *temp_len, size_t random_len,
size_t actual_alloc);
/**
* lws_jws_alloc_element() - create an element and reserve space for content
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param len: logical length of element
* \param actual_alloc: 0 for same as len, else actual allocation size
*
* Allocate len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the element logical length will be set to len.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_alloc_element(struct lws_jws_map *map, int idx, char *temp,
int *temp_len, size_t len, size_t actual_alloc);
/**
* lws_jws_encode_b64_element() - create an b64-encoded element
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param in: pointer to unencoded input
* \param in_len: length of unencoded input
*
* Allocate len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the element logical length will be set to len.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_encode_b64_element(struct lws_jws_map *map, int idx,
char *temp, int *temp_len, const void *in,
size_t in_len);
/**
* lws_jws_b64_compact_map() - find block starts and lengths in compact b64
*
* \param in: pointer to b64 jose.payload[.hdr].sig
* \param len: bytes available at \p in
* \param map: output struct with pointers and lengths for each JWS element
*
* Scans a jose.payload[.hdr].sig b64 string and notes where the blocks start
* and their length into \p map.
*
* Returns number of blocks if OK. May return <0 if malformed.
* May not fill all map entries.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_map *map);
/**
* lws_jws_base64_enc() - encode input data into b64url data
*
* \param in: the incoming plaintext
* \param in_len: the length of the incoming plaintext in bytes
* \param out: the buffer to store the b64url encoded data to
* \param out_max: the length of \p out in bytes
*
* Returns either -1 if problems, or the number of bytes written to \p out.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
/**
* lws_jws_encode_section() - encode input data into b64url data,
* prepending . if not first
*
* \param in: the incoming plaintext
* \param in_len: the length of the incoming plaintext in bytes
* \param first: nonzero if the first section
* \param p: the buffer to store the b64url encoded data to
* \param end: just past the end of p
*
* Returns either -1 if problems, or the number of bytes written to \p out.
* If the section is not the first one, '.' is prepended.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
char *end);
/**
* lws_jwt_signed_validate() - check a compact JWT against a key and alg
*
* \param ctx: the lws_context
* \param jwk: the key for checking the signature
* \param alg_list: the expected alg name, like "ES512"
* \param com: the compact JWT
* \param len: the length of com
* \param temp: a temp scratchpad
* \param tl: available length of temp scratchpad
* \param out: the output buffer to hold the validated plaintext
* \param out_len: on entry, max length of out; on exit, used length of out
*
* Returns nonzero if the JWT cannot be validated or the plaintext can't fit the
* provided output buffer, or 0 if it is validated as being signed by the
* provided jwk.
*
* If validated, the plaintext in the JWT is copied into out and out_len set to
* the used length.
*
* temp can be discarded or reused after the call returned, it's used to hold
* transformations of the B64 JWS in the JWT.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg_list, const char *com, size_t len,
char *temp, int tl, char *out, size_t *out_len);
/**
* lws_jwt_sign_compact() - generate a compact JWT using a key and alg
*
* \param ctx: the lws_context
* \param jwk: the signing key
* \param alg: the signing alg name, like "ES512"
* \param out: the output buffer to hold the signed JWT in compact form
* \param out_len: on entry, the length of out; on exit, the used amount of out
* \param temp: a temp scratchpad
* \param tl: available length of temp scratchpad
* \param format: a printf style format specification
* \param ...: zero or more args for the format specification
*
* Creates a JWT in a single step, from the format string and args through to
* outputting a well-formed compact JWT representation in out.
*
* Returns 0 if all is well and *out_len is the amount of data in out, else
* nonzero if failed. Temp must be large enough to hold various intermediate
* representations.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk,
const char *alg, char *out, size_t *out_len, char *temp,
int tl, const char *format, ...) LWS_FORMAT(8);
struct lws_jwt_sign_info {
const char *alg;
/**< entry: signing alg name, like "RS256" */
const char *jose_hdr;
/**< entry: optional JOSE hdr; if present, alg field is ignored; instead the
* whole claim object has to be provided in this parameter */
size_t jose_hdr_len;
/**< entry: if jose_hdr is not NULL, JOSE header length without terminating '\0' */
char *out;
/**< exit: signed JWT in compact form*/
size_t *out_len;
/**< entry,exit: buffer size of out; actual size of JWT on exit */
char *temp;
/**< exit undefined content, used by the function as a temporary scratchpad; MUST
* be large enogh to store various intermediate representations */
int tl;
/**< entry: size of temp buffer */
};
/**
* lws_jwt_sign_via_info() - generate a compact JWT using a key and JOSE header
*
* \param ctx: the lws_context
* \param jwk: the signing key
* \param info: info describing the JWT's content and output/temp buffers
* \param format: a printf style format specification of the claims object
* \param ...: zero or more args for the format specification
*
* Creates a JWT in a single step, from the format string and args through to
* outputting a well-formed compact JWT representation in out. The provided
* JOSE header's syntax is checked before it is added to the JWT.
*
* Returns 0 if all is well and *out_len is the amount of data in out, else
* nonzero if failed. Temp must be large enough to hold various intermediate
* representations.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_sign_via_info(struct lws_context *ctx, struct lws_jwk *jwk,
const struct lws_jwt_sign_info *info, const char *format, ...) LWS_FORMAT(4);
/**
* lws_jwt_token_sanity() - check a validated jwt payload for sanity
*
* \param in: the JWT payload
* \param in_len: the length of the JWT payload
* \param iss: the expected issuer of the token
* \param aud: the expected audience of the token
* \param csrf_in: NULL, or the csrf token that came in on a URL
* \param sub: a buffer to hold the subject name in the JWT (eg, account name)
* \param sub_len: the max length of the sub buffer
* \param secs_left: set to the number of seconds of valid auth left if valid
*
* This performs some generic sanity tests on validated JWT payload...
*
* - the issuer is as expected
* - the audience is us
* - current time is OK for nbf ("not before") in the token
* - current time is OK for exp ("expiry") in the token
* - if csrf_in is not NULL, that the JWK has a csrf and it matches it
* - if sub is not NULL, that the JWK provides a subject (and copies it to sub)
*
* If the tests pass, *secs_left is set to the number of remaining seconds the
* auth is valid.
*
* Returns 0 if no inconsistency, else nonzero.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_token_sanity(const char *in, size_t in_len,
const char *iss, const char *aud, const char *csrf_in,
char *sub, size_t sub_len, unsigned long *exp_unix_time);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
struct lws_jwt_sign_set_cookie {
struct lws_jwk *jwk;
/**< entry: required signing key */
const char *alg;
/**< entry: required signing alg, eg, "ES512" */
const char *iss;
/**< entry: issuer name to use */
const char *aud;
/**< entry: audience */
const char *cookie_name;
/**< entry: the name of the cookie */
char sub[33];
/**< sign-entry, validate-exit: subject */
const char *extra_json;
/**< sign-entry, validate-exit:
* optional "ext" JSON object contents for the JWT */
size_t extra_json_len;
/**< validate-exit:
* length of optional "ext" JSON object contents for the JWT */
const char *csrf_in;
/**< validate-entry:
* NULL, or an external CSRF token to check against what is in the JWT */
unsigned long expiry_unix_time;
/**< sign-entry: seconds the JWT and cookie may live,
* validate-exit: expiry unix time */
};
/**
* lws_jwt_sign_token_set_http_cookie() - creates sets a JWT in a wsi cookie
*
* \param wsi: the wsi to create the cookie header on
* \param i: structure describing what should be in the JWT
* \param p: wsi headers area
* \param end: end of wsi headers area
*
* Creates a JWT specified \p i, and attaches it to the outgoing headers on
* wsi. Returns 0 if successful.
*
* Best-practice security restrictions are applied to the cookie set action,
* including forcing httponly, and __Host- prefix. As required by __Host-, the
* cookie Path is set to /. __Host- is applied by the function, the cookie_name
* should just be "xyz" for "__Host-xyz".
*
* \p extra_json should just be the bare JSON, a { } is provided around it by
* the function if it's non-NULL. For example, "\"authorization\": 1".
*
* It's recommended the secs parameter is kept as small as consistent with one
* user session on the site if possible, eg, 10 minutes or 20 minutes. At the
* server, it can determine how much time is left in the auth and inform the
* client; if the JWT validity expires, the page should reload so the UI always
* reflects what's possible to do with the authorization state correctly. If
* the JWT expires, the user can log back in using credentials usually stored in
* the browser and auto-filled-in, so this is not very inconvenient.
*
* This is a helper on top of the other JOSE and JWT apis that somewhat crosses
* over between JWT and HTTP, since it knows about cookies. So it is only built
* if both LWS_WITH_JOSE and one of the http-related roles enabled.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwt_sign_token_set_http_cookie(struct lws *wsi,
const struct lws_jwt_sign_set_cookie *i,
uint8_t **p, uint8_t *end);
LWS_VISIBLE LWS_EXTERN int
lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi,
struct lws_jwt_sign_set_cookie *i,
char *out, size_t *out_len);
#endif
///@}

View File

@ -0,0 +1,539 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup lecp CBOR parser
* ##CBOR parsing related functions
* \ingroup lwsapi
*
* LECP is an extremely lightweight CBOR stream parser included in lws. It
* is aligned in approach with the LEJP JSON stream parser, with some additional
* things needed for CBOR.
*/
//@{
#ifndef LECP_MAX_PARSING_STACK_DEPTH
#define LECP_MAX_PARSING_STACK_DEPTH 5
#endif
#ifndef LECP_MAX_DEPTH
#define LECP_MAX_DEPTH 12
#endif
#ifndef LECP_MAX_INDEX_DEPTH
#define LECP_MAX_INDEX_DEPTH 8
#endif
#ifndef LECP_MAX_PATH
#define LECP_MAX_PATH 128
#endif
#ifndef LECP_STRING_CHUNK
/* must be >= 30 to assemble floats */
#define LECP_STRING_CHUNK 254
#endif
#define LECP_FLAG_CB_IS_VALUE 64
/*
* CBOR initial byte 3 x MSB bits are these
*/
enum {
LWS_CBOR_MAJTYP_UINT = 0 << 5,
LWS_CBOR_MAJTYP_INT_NEG = 1 << 5,
LWS_CBOR_MAJTYP_BSTR = 2 << 5,
LWS_CBOR_MAJTYP_TSTR = 3 << 5,
LWS_CBOR_MAJTYP_ARRAY = 4 << 5,
LWS_CBOR_MAJTYP_MAP = 5 << 5,
LWS_CBOR_MAJTYP_TAG = 6 << 5,
LWS_CBOR_MAJTYP_FLOAT = 7 << 5, /* also BREAK */
LWS_CBOR_MAJTYP_MASK = 7 << 5,
/*
* For the low 5 bits of the opcode, 0-23 are literals, unless it's
* FLOAT.
*
* 24 = 1 byte; 25 = 2..., 26 = 4... and 27 = 8 bytes following literal.
*/
LWS_CBOR_1 = 24,
LWS_CBOR_2 = 25,
LWS_CBOR_4 = 26,
LWS_CBOR_8 = 27,
LWS_CBOR_RESERVED = 28,
LWS_CBOR_SUBMASK = 0x1f,
/*
* Major type 7 discriminators in low 5 bits
* 0 - 23 is SIMPLE implicit value (like, eg, LWS_CBOR_SWK_TRUE)
*/
LWS_CBOR_SWK_FALSE = 20,
LWS_CBOR_SWK_TRUE = 21,
LWS_CBOR_SWK_NULL = 22,
LWS_CBOR_SWK_UNDEFINED = 23,
LWS_CBOR_M7_SUBTYP_SIMPLE_X8 = 24, /* simple with additional byte */
LWS_CBOR_M7_SUBTYP_FLOAT16 = 25,
LWS_CBOR_M7_SUBTYP_FLOAT32 = 26,
LWS_CBOR_M7_SUBTYP_FLOAT64 = 27,
LWS_CBOR_M7_BREAK = 31,
/* 28, 29, 30 are illegal.
*
* 31 is illegal for UINT, INT_NEG, and TAG;
* for BSTR, TSTR, ARRAY and MAP it means "indefinite length", ie,
* it's made up of an endless amount of determinite-length
* fragments terminated with a BREAK (FLOAT | 31) instead of the
* next determinite-length fragment. The second framing level
* means no need for escapes for BREAK in the data.
*/
LWS_CBOR_INDETERMINITE = 31,
/*
* Well-known tags
*/
LWS_CBOR_WKTAG_DATETIME_STD = 0, /* text */
LWS_CBOR_WKTAG_DATETIME_EPOCH = 1, /* int or float */
LWS_CBOR_WKTAG_BIGNUM_UNSIGNED = 2, /* byte string */
LWS_CBOR_WKTAG_BIGNUM_NEGATIVE = 3, /* byte string */
LWS_CBOR_WKTAG_DECIMAL_FRAC = 4, /* array */
LWS_CBOR_WKTAG_BIGFLOAT = 5, /* array */
LWS_CBOR_WKTAG_COSE_ENC0 = 16,
LWS_CBOR_WKTAG_COSE_MAC0 = 17,
LWS_CBOR_WKTAG_COSE_SIGN1 = 18,
LWS_CBOR_WKTAG_TO_B64U = 21, /* any */
LWS_CBOR_WKTAG_TO_B64 = 22, /* any */
LWS_CBOR_WKTAG_TO_B16 = 23, /* any */
LWS_CBOR_WKTAG_CBOR = 24, /* byte string */
LWS_CBOR_WKTAG_URI = 32, /* text string */
LWS_CBOR_WKTAG_B64U = 33, /* text string */
LWS_CBOR_WKTAG_B64 = 34, /* text string */
LWS_CBOR_WKTAG_MIME = 36, /* text string */
LWS_CBOR_WKTAG_COSE_ENC = 96,
LWS_CBOR_WKTAG_COSE_MAC = 97,
LWS_CBOR_WKTAG_COSE_SIGN = 98,
LWS_CBOR_WKTAG_SELFDESCCBOR = 55799
};
enum lecp_callbacks {
LECPCB_CONSTRUCTED = 0,
LECPCB_DESTRUCTED = 1,
LECPCB_COMPLETE = 3,
LECPCB_FAILED = 4,
LECPCB_PAIR_NAME = 5,
LECPCB_VAL_TRUE = LECP_FLAG_CB_IS_VALUE | 6,
LECPCB_VAL_FALSE = LECP_FLAG_CB_IS_VALUE | 7,
LECPCB_VAL_NULL = LECP_FLAG_CB_IS_VALUE | 8,
LECPCB_VAL_NUM_INT = LECP_FLAG_CB_IS_VALUE | 9,
LECPCB_VAL_RESERVED = LECP_FLAG_CB_IS_VALUE | 10,
LECPCB_VAL_STR_START = 11, /* notice handle separately */
LECPCB_VAL_STR_CHUNK = LECP_FLAG_CB_IS_VALUE | 12,
LECPCB_VAL_STR_END = LECP_FLAG_CB_IS_VALUE | 13,
LECPCB_ARRAY_START = 14,
LECPCB_ARRAY_END = 15,
LECPCB_OBJECT_START = 16,
LECPCB_OBJECT_END = 17,
LECPCB_TAG_START = 18,
LECPCB_TAG_END = 19,
LECPCB_VAL_NUM_UINT = LECP_FLAG_CB_IS_VALUE | 20,
LECPCB_VAL_UNDEFINED = LECP_FLAG_CB_IS_VALUE | 21,
LECPCB_VAL_FLOAT16 = LECP_FLAG_CB_IS_VALUE | 22,
LECPCB_VAL_FLOAT32 = LECP_FLAG_CB_IS_VALUE | 23,
LECPCB_VAL_FLOAT64 = LECP_FLAG_CB_IS_VALUE | 24,
LECPCB_VAL_SIMPLE = LECP_FLAG_CB_IS_VALUE | 25,
LECPCB_VAL_BLOB_START = 26, /* notice handle separately */
LECPCB_VAL_BLOB_CHUNK = LECP_FLAG_CB_IS_VALUE | 27,
LECPCB_VAL_BLOB_END = LECP_FLAG_CB_IS_VALUE | 28,
LECPCB_ARRAY_ITEM_START = 29,
LECPCB_ARRAY_ITEM_END = 30,
LECPCB_LITERAL_CBOR = 31,
};
enum lecp_reasons {
LECP_CONTINUE = -1,
LECP_REJECT_BAD_CODING = -2,
LECP_REJECT_UNKNOWN = -3,
LECP_REJECT_CALLBACK = -4,
LECP_STACK_OVERFLOW = -5,
};
struct lecp_item {
union {
uint64_t u64;
int64_t i64;
uint64_t u32;
uint16_t hf;
#if defined(LWS_WITH_CBOR_FLOAT)
float f;
double d;
#else
uint32_t f;
uint64_t d;
#endif
} u;
uint8_t opcode;
};
struct lecp_ctx;
typedef signed char (*lecp_callback)(struct lecp_ctx *ctx, char reason);
struct _lecp_stack {
char s; /* lejp_state stack*/
uint8_t p; /* path length */
char i; /* index array length */
char indet; /* indeterminite */
char intermediate; /* in middle of string */
char pop_iss;
uint64_t tag;
uint64_t collect_rem;
uint32_t ordinal;
uint8_t opcode;
uint8_t send_new_array_item;
uint8_t barrier;
};
struct _lecp_parsing_stack {
void *user; /* private to the stack level */
lecp_callback cb;
const char * const *paths;
uint8_t count_paths;
uint8_t ppos;
uint8_t path_match;
};
struct lecp_ctx {
/* sorted by type for most compact alignment
*
* pointers
*/
void *user;
uint8_t *collect_tgt;
/* arrays */
struct _lecp_parsing_stack pst[LECP_MAX_PARSING_STACK_DEPTH];
struct _lecp_stack st[LECP_MAX_DEPTH];
uint16_t i[LECP_MAX_INDEX_DEPTH]; /* index array */
uint16_t wild[LECP_MAX_INDEX_DEPTH]; /* index array */
char path[LECP_MAX_PATH];
uint8_t cbor[64]; /* literal cbor capture */
struct lecp_item item;
/* size_t */
size_t path_stride; /* 0 means default ptr size, else
* stride... allows paths to be
* provided composed inside a
* larger user struct instead of a
* duplicated array */
size_t used_in; /* bytes of input consumed */
/* short */
uint16_t uni;
/* char */
uint8_t npos;
uint8_t dcount;
uint8_t f;
uint8_t sp; /* stack head */
uint8_t ipos; /* index stack depth */
uint8_t count_paths;
uint8_t path_match;
uint8_t path_match_len;
uint8_t wildcount;
uint8_t pst_sp; /* parsing stack head */
uint8_t outer_array;
uint8_t cbor_pos;
uint8_t literal_cbor_report;
char present; /* temp for cb reason to use */
uint8_t be; /* big endian */
/* at end so we can memset the rest of it */
char buf[LECP_STRING_CHUNK + 1];
};
enum lws_lec_pctx_ret {
LWS_LECPCTX_RET_FINISHED = 0,
LWS_LECPCTX_RET_AGAIN, /* call again to continue writing buffer */
LWS_LECPCTX_RET_FAIL /* something broken, eg, format string */
};
enum cbp_state {
CBPS_IDLE,
CBPS_PC1,
CBPS_PC2,
CBPS_PC3,
CBPS_STRING_BODY,
CBPS_NUM_LIT,
CBPS_STRING_LIT,
CBPS_CONTYPE,
};
typedef struct lws_lec_pctx {
uint8_t stack[16];
uint8_t vaa[16];
uint8_t indet[16];
uint8_t scratch[24];
uint8_t *start; /* the beginning of the out buf */
uint8_t *buf; /* cur pos in output buf */
uint8_t *end; /* the end of the output buf */
const uint8_t *ongoing_src;
uint64_t ongoing_len;
uint64_t ongoing_done;
struct lecp_item item;
size_t used; /* number of bytes valid from start */
int opaque[4]; /* ignored by lws, caller may use */
enum cbp_state state;
unsigned int fmt_pos;
uint8_t sp;
uint8_t scratch_len;
uint8_t escflag;
uint8_t _long;
uint8_t vaa_pos;
uint8_t dotstar;
} lws_lec_pctx_t;
LWS_VISIBLE LWS_EXTERN void
lws_lec_int(lws_lec_pctx_t *ctx, uint8_t opcode, uint8_t indet, uint64_t num);
LWS_VISIBLE LWS_EXTERN int
lws_lec_scratch(lws_lec_pctx_t *ctx);
/*
* lws_lec_init() - prepare a cbor writing context
*
* \param ctx: the cbor writing context to prepare
* \param buf: the output buffer start
* \param len: the amount of the output buffer we can use
*
* Prepares a cbor writing context so that les_lec_printf can be used to
* write into it.
*/
LWS_VISIBLE LWS_EXTERN void
lws_lec_init(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len);
/*
* lws_lec_setbuf() - update the output buffer for an initialized cbor writing ctx
*
* \param ctx: the cbor writing context to prepare
* \param buf: the output buffer start
* \param len: the amount of the output buffer we can use
*
* Leaves the cbor writing context state as it is, but resets the output buffer
* it writes into as given in \p buf and \p len
*/
LWS_VISIBLE LWS_EXTERN void
lws_lec_setbuf(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len);
/*
* lws_lec_vsprintf() - write into a cbor writing context
*
* \param ctx: the cbor writing context to prepare
* \param format: a printf style argument map
* \param args: the va args
*
* CBOR-aware vsprintf which pauses output when it fills the output buffer. You
* can call it again with the same args and same lws_lex_pctx to resume filling
*
* Returns either LWS_LECPCTX_RET_FINISHED if we have nothing left over that we
* want to put in the buffer, or LWS_LECPCTX_RET_AGAIN if the function should
* be called again with the same arguments (perhaps into a different output
* buffer) to continue emitting output from where it left off.
*
* If LWS_LECPCTX_RET_AGAIN is returned, lws_lec_setbuf() must be used on the
* context to reset or change the output buffer before calling again.
*
* The number of bytes placed in the output buffer is available in ctx->used.
*
* \p format is a printf-type format string that is specialized for CBOR
* generation. It understands the following specifiers
*
* |`123`||unsigned literal number|
* |`-123`||signed literal number|
* |`%u`|`unsigned int`|number|
* |`%lu`|`unsigned long int`|number|
* |`%llu`|`unsigned long long int`|number|
* |`%d`|`signed int`|number|
* |`%ld`|`signed long int`|number|
* |`%lld`|`signed long long int`|number|
* |`%f`|`double`|floating point number|
* |`123(...)`||literal tag and scope|
* |`%t(...)`|`unsigned int`|tag and scope|
* |`%lt(...)`|`unsigned long int`|tag and scope|
* |`%llt(...)`|`unsigned long long int`|tag and scope|
* |`[...]`||Array (fixed len if `]` in same format string)|
* |`{...}`||Map (fixed len if `}` in same format string)|
* |`<t...>`||Container for indeterminite text string frags|
* |`<b...>`||Container for indeterminite binary string frags|
* |`'string'`||Literal text of known length|
* |`%s`|`const char *`|NUL-terminated string|
* |`%.*s`|`int`, `const char *`|length-specified string|
* |`%.*b`|`int`, `const uint8_t *`|length-specified binary|
* |`:`||separator between Map items (a:b)|
* |`,`||separator between Map pairs or array items|
*
* See READMEs/README.cbor-lecp.md for more details.
*/
LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret
lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *format, va_list args);
/*
* lws_lec_printf() - write into a cbor writing context
*
* \param ctx: the cbor writing context to prepare
* \param format: a printf style argument map
* \param ...: format args
*
* See lws_lec_vsprintf() for format details. This is the most common way
* to format the CBOR output.
*
* See READMEs/README.cbor-lecp.md for more details.
*/
LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret
lws_lec_printf(lws_lec_pctx_t *ctx, const char *format, ...);
/**
* lecp_construct() - Construct an LECP parser context
*
* \param ctx: the parser context object to be initialized
* \param cb: the user callback to receive the parsing events
* \param user: an opaque user pointer available at \p cb
* \param paths: an optional array of parsing paths
* \param paths_count: how many paths in \p paths
*
* Prepares an LECP parser context for parsing.
*/
LWS_VISIBLE LWS_EXTERN void
lecp_construct(struct lecp_ctx *ctx, lecp_callback cb, void *user,
const char * const *paths, unsigned char paths_count);
/**
* lecp_destruct() - Destroys an LECP parser context
*
* \param ctx: the parser context object to be destroyed
*/
LWS_VISIBLE LWS_EXTERN void
lecp_destruct(struct lecp_ctx *ctx);
/**
* lecp_parse() - parses a chunk of input CBOR
*
* \p ctx: the parsing context
* \p cbor: the start of the chunk of CBOR
* \p len: the number of bytes of CBOR available at \p cbor
*
* Returns LECP_CONTINUE if more input needed, one of enum lecp_reasons for a
* fatal error, else 0 for successful parsing completion.
*
* On success or _CONTINUE, ctx->used_in is set to the number of input bytes
* consumed.
*/
LWS_VISIBLE LWS_EXTERN int
lecp_parse(struct lecp_ctx *ctx, const uint8_t *cbor, size_t len);
LWS_VISIBLE LWS_EXTERN void
lecp_change_callback(struct lecp_ctx *ctx, lecp_callback cb);
LWS_VISIBLE LWS_EXTERN const char *
lecp_error_to_string(int e);
/**
* lecp_parse_report_raw() - turn cbor raw reporting on and off
*
* \param ctx: the lecp context
* \param on: 0 to disable (defaults disabled), 1 to enable
*
* For cose_sign, it needs access to raw cbor subtrees for the hash input.
* This api causes LECPCB_LITERAL_CBOR parse callbacks when there are
* ctx->cbor_pos bytes of raw cbor available in ctx->cbor[]. the callbacks
* occur when the ctx->cbor[] buffer fills or if it holds anything when this
* spi is used to stop the reports.
*
* The same CBOR that is being captured continues to be passed for parsing.
*/
LWS_VISIBLE LWS_EXTERN void
lecp_parse_report_raw(struct lecp_ctx *ctx, int on);
/**
* lecp_parse_map_is_key() - return nonzero if we're in a map and this is a key
*
* \param ctx: the lwcp context
*
* Checks if the current value is a key in a map, ie, that you are on a "key" in
* a list of "{key: value}" pairs. Zero means you're either not in a map or not
* on the key part, and nonzero means you are in a map and on a key part.
*/
LWS_VISIBLE LWS_EXTERN int
lecp_parse_map_is_key(struct lecp_ctx *ctx);
LWS_VISIBLE LWS_EXTERN int
lecp_parse_subtree(struct lecp_ctx *ctx, const uint8_t *in, size_t len);
/*
* Helpers for half-float
*/
LWS_VISIBLE LWS_EXTERN void
lws_singles2halfp(uint16_t *hp, uint32_t x);
LWS_VISIBLE LWS_EXTERN void
lws_halfp2singles(uint32_t *xp, uint16_t h);
//@}

View File

@ -0,0 +1,146 @@
/*
* Generic LED controller ops
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is like an abstract class for leds, a real implementation provides
* functions for the ops that use the underlying, eg, OS gpio arrangements.
*/
/* only b15 significant for GPIO */
typedef uint16_t lws_led_intensity_t;
typedef uint16_t lws_led_seq_phase_t;
/* the normalized max intensity */
#define LWS_LED_MAX_INTENSITY (0xffff)
/* the normalized 360 degree phase count for intensity functions */
#define LWS_LED_FUNC_PHASE 65536
/* used when the sequence doesn't stop by itself and goes around forever */
#define LWS_SEQ_LEDPHASE_TOTAL_ENDLESS (-1)
#define LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS 33
struct lws_led_state; /* opaque */
struct lws_pwm_ops; /* forward ref */
typedef lws_led_intensity_t (*lws_led_lookup_t)(lws_led_seq_phase_t ph);
typedef struct lws_led_sequence_def_t {
lws_led_lookup_t func;
lws_led_seq_phase_t ledphase_offset;
int ledphase_total; /* 65536= one cycle */
uint16_t ms;
uint8_t flags;
} lws_led_sequence_def_t;
enum {
LLSI_CURR,
LLSI_NEXT,
LLSI_TRANS
};
typedef struct lws_led_state_ch
{
const lws_led_sequence_def_t *seq; /* NULL = inactive */
lws_led_seq_phase_t ph;
lws_led_seq_phase_t step;
int phase_budget;
lws_led_intensity_t last;
/**< at the end of the sequence we decouple the sequencer, but leave
* the last computed sample behind for further transitions to base off
*/
} lws_led_state_ch_t;
typedef struct lws_led_state_chs
{
lws_led_state_ch_t seqs[3];
} lws_led_state_chs_t;
/* this should always be first in the subclassed implementation types */
typedef struct lws_led_ops {
void (*intensity)(const struct lws_led_ops *lo, const char *name,
lws_led_intensity_t inten);
/**< for BOOL led control like GPIO, only inten b15 is significant */
struct lws_led_state * (*create)(const struct lws_led_ops *led_ops);
void (*destroy)(struct lws_led_state *);
} lws_led_ops_t;
typedef struct lws_led_gpio_map {
const char *name;
_lws_plat_gpio_t gpio;
lws_led_lookup_t intensity_correction;
/**< May be NULL. If GPIO-based LED, ignored. If pwm_ops provided,
* NULL means use default CIE 100% correction function. If non-NULL,
* use the pointed-to correction function. This is useful to provide
* LED-specific intensity correction / scaling so different types of
* LED can "look the same". */
const struct lws_pwm_ops *pwm_ops;
/**< if NULL, gpio controls the led directly. If set to a pwm_ops,
* the led control is outsourced to the pwm controller. */
uint8_t active_level;
} lws_led_gpio_map_t;
typedef struct lws_led_gpio_controller {
const lws_led_ops_t led_ops;
const lws_gpio_ops_t *gpio_ops;
const lws_led_gpio_map_t *led_map;
uint8_t count_leds;
} lws_led_gpio_controller_t;
/* ops */
LWS_VISIBLE LWS_EXTERN struct lws_led_state *
lws_led_gpio_create(const lws_led_ops_t *led_ops);
LWS_VISIBLE LWS_EXTERN void
lws_led_gpio_destroy(struct lws_led_state *lcs);
/**
* lws_led_gpio_intensity() - set the static intensity of an led
*
* \param lo: the base class of the led controller
* \param index: which led in the controller set
* \param inten: 16-bit unsigned intensity
*
* For LEDs controlled by a BOOL like GPIO, only inten b15 is significant.
* For PWM type LED control, as many bits as the hardware can support from b15
* down are significant.
*/
LWS_VISIBLE LWS_EXTERN void
lws_led_gpio_intensity(const struct lws_led_ops *lo, const char *name,
lws_led_intensity_t inten);
LWS_VISIBLE LWS_EXTERN int
lws_led_transition(struct lws_led_state *lcs, const char *name,
const lws_led_sequence_def_t *next,
const lws_led_sequence_def_t *trans);
#define lws_led_gpio_ops \
{ \
.create = lws_led_gpio_create, \
.destroy = lws_led_gpio_destroy, \
.intensity = lws_led_gpio_intensity, \
}

Some files were not shown because too many files have changed in this diff Show More