Files
yoga/tests/YGPersistenceTest.cpp

292 lines
10 KiB
C++
Raw Permalink Normal View History

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
*/
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
#include <gtest/gtest.h>
#include <yoga/Yoga.h>
C++ Cleanup 3/N: Reorganize YGNode (#1350) Summary: X-link: https://github.com/facebook/react-native/pull/39219 Pull Request resolved: https://github.com/facebook/yoga/pull/1350 X-link: https://github.com/facebook/react-native/pull/39170 ## This diff This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`). The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it. This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs. ## This stack The organization of the C++ internals of Yoga are in need of attention. 1. Some of the C++ internals are namespaced, but others not. 2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these) 2. Most of the files are in a flat hierarchy, except for event tracing in its own folder 3. Some files and functions begin with YG, others don’t 4. Some functions are uppercase, others are not 5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about 6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h) 7. There is no clear indication from file structure or type naming what is private vs not 8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers This stack does some much needed spring cleaning: 1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy 3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended 4. Utils files are split 5. Most C++ internals drop the YG prefix 6. Most C++ internal function names are all lower camel case 7. We start to split up Yoga.cpp 8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings 9. It is not possible to use private APIs without static casting handles to internal classes This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well. These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer. Changelog: [Internal] bypass-github-export-checks Reviewed By: shwanton Differential Revision: D48847258 fbshipit-source-id: fc560893533b55a5c2d52c37d8e9a59f7369f174
2023-08-30 19:57:16 -07:00
#include <yoga/node/Node.h>
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
#include "util/TestUtil.h"
C++ Cleanup 3/N: Reorganize YGNode (#1350) Summary: X-link: https://github.com/facebook/react-native/pull/39219 Pull Request resolved: https://github.com/facebook/yoga/pull/1350 X-link: https://github.com/facebook/react-native/pull/39170 ## This diff This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`). The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it. This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs. ## This stack The organization of the C++ internals of Yoga are in need of attention. 1. Some of the C++ internals are namespaced, but others not. 2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these) 2. Most of the files are in a flat hierarchy, except for event tracing in its own folder 3. Some files and functions begin with YG, others don’t 4. Some functions are uppercase, others are not 5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about 6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h) 7. There is no clear indication from file structure or type naming what is private vs not 8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers This stack does some much needed spring cleaning: 1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy 3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended 4. Utils files are split 5. Most C++ internals drop the YG prefix 6. Most C++ internal function names are all lower camel case 7. We start to split up Yoga.cpp 8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings 9. It is not possible to use private APIs without static casting handles to internal classes This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well. These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer. Changelog: [Internal] bypass-github-export-checks Reviewed By: shwanton Differential Revision: D48847258 fbshipit-source-id: fc560893533b55a5c2d52c37d8e9a59f7369f174
2023-08-30 19:57:16 -07:00
using namespace facebook;
using facebook::yoga::test::TestUtil;
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
TEST(YogaTest, cloning_shared_root) {
YGConfigRef config = YGConfigNew();
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetWidth(root, 100);
YGNodeStyleSetHeight(root, 100);
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexGrow(root_child0, 1);
YGNodeStyleSetFlexBasis(root_child0, 50);
YGNodeInsertChild(root, root_child0, 0);
YGNodeRef root_child1 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexGrow(root_child1, 1);
YGNodeInsertChild(root, root_child1, 1);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0));
ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1));
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1));
YGNodeRef root2 = YGNodeClone(root);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetWidth(root2, 100);
ASSERT_EQ(2u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
// The children should have referential equality at this point.
ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0));
ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1));
YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_EQ(2u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
// Relayout with no changed input should result in referential equality.
ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0));
ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1));
YGNodeStyleSetWidth(root2, 150);
YGNodeStyleSetHeight(root2, 200);
YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_EQ(2u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
// Relayout with changed input should result in cloned children.
YGNodeRef root2_child0 = YGNodeGetChild(root2, 0);
YGNodeRef root2_child1 = YGNodeGetChild(root2, 1);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
ASSERT_NE(root_child0, root2_child0);
ASSERT_NE(root_child1, root2_child1);
// Everything in the root should remain unchanged.
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0));
ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1));
ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1));
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1));
// The new root now has new layout.
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2));
ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2));
ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root2));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2_child0));
ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child0));
ASSERT_FLOAT_EQ(125, YGNodeLayoutGetHeight(root2_child0));
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child1));
ASSERT_FLOAT_EQ(125, YGNodeLayoutGetTop(root2_child1));
ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child1));
ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root2_child1));
YGNodeFreeRecursive(root2);
YGNodeFreeRecursive(root);
YGConfigFree(config);
}
TEST(YogaTest, mutating_children_of_a_clone_clones_only_after_layout) {
YGConfigRef config = YGConfigNew();
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root = YGNodeNewWithConfig(config);
ASSERT_EQ(0u, YGNodeGetChildCount(root));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root2 = YGNodeClone(root);
ASSERT_EQ(0u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root2_child0 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeInsertChild(root2, root2_child0, 0);
ASSERT_EQ(0u, YGNodeGetChildCount(root));
ASSERT_EQ(1u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root3 = YGNodeClone(root2);
ASSERT_EQ(1u, YGNodeGetChildCount(root2));
ASSERT_EQ(1u, YGNodeGetChildCount(root3));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
ASSERT_EQ(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
YGNodeRef root3_child1 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeInsertChild(root3, root3_child1, 1);
ASSERT_EQ(1u, YGNodeGetChildCount(root2));
ASSERT_EQ(2u, YGNodeGetChildCount(root3));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
ASSERT_EQ(root3_child1, YGNodeGetChild(root3, 1));
ASSERT_EQ(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root4 = YGNodeClone(root3);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
ASSERT_EQ(root3_child1, YGNodeGetChild(root4, 1));
YGNodeRemoveChild(root4, root3_child1);
ASSERT_EQ(2u, YGNodeGetChildCount(root3));
ASSERT_EQ(1u, YGNodeGetChildCount(root4));
ASSERT_EQ(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0));
YGNodeCalculateLayout(root4, YGUndefined, YGUndefined, YGDirectionLTR);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
ASSERT_NE(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0));
YGNodeCalculateLayout(root3, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_NE(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeFreeRecursive(root4);
YGNodeFreeRecursive(root3);
YGNodeFreeRecursive(root2);
YGNodeFreeRecursive(root);
YGConfigFree(config);
}
TEST(YogaTest, cloning_two_levels) {
YGConfigRef config = YGConfigNew();
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetWidth(root, 100);
YGNodeStyleSetHeight(root, 100);
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexGrow(root_child0, 1);
YGNodeStyleSetFlexBasis(root_child0, 15);
YGNodeInsertChild(root, root_child0, 0);
YGNodeRef root_child1 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexGrow(root_child1, 1);
YGNodeInsertChild(root, root_child1, 1);
YGNodeRef root_child1_0 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexBasis(root_child1_0, 10);
YGNodeStyleSetFlexGrow(root_child1_0, 1);
YGNodeInsertChild(root_child1, root_child1_0, 0);
YGNodeRef root_child1_1 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexBasis(root_child1_1, 25);
YGNodeInsertChild(root_child1, root_child1_1, 1);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0));
ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1));
ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0));
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1));
YGNodeRef root2_child0 = YGNodeClone(root_child0);
YGNodeRef root2_child1 = YGNodeClone(root_child1);
YGNodeRef root2 = YGNodeClone(root);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetFlexGrow(root2_child0, 0);
YGNodeStyleSetFlexBasis(root2_child0, 40);
YGNodeRemoveAllChildren(root2);
YGNodeInsertChild(root2, root2_child0, 0);
YGNodeInsertChild(root2, root2_child1, 1);
ASSERT_EQ(2u, YGNodeGetChildCount(root2));
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);
// Original root is unchanged
ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0));
ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1));
ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0));
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1));
// New root has new layout at the top
ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root2_child0));
ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root2_child1));
// The deeper children are untouched.
ASSERT_EQ(YGNodeGetChild(root2_child1, 0), root_child1_0);
ASSERT_EQ(YGNodeGetChild(root2_child1, 1), root_child1_1);
YGNodeFreeRecursive(root2);
YGNodeFreeRecursive(root);
YGConfigFree(config);
}
TEST(YogaTest, cloning_and_freeing) {
TestUtil::startCountingNodes();
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGConfigRef config = YGConfigNew();
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeRef root = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeStyleSetWidth(root, 100);
YGNodeStyleSetHeight(root, 100);
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeInsertChild(root, root_child0, 0);
YGNodeRef root_child1 = YGNodeNewWithConfig(config);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeInsertChild(root, root_child1, 1);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
YGNodeRef root2 = YGNodeClone(root);
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
// Freeing the original root should be safe as long as we don't free its
// children.
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
YGNodeFree(root);
YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR);
YGNodeFreeRecursive(root2);
YGNodeFree(root_child0);
YGNodeFree(root_child1);
YGConfigFree(config);
ASSERT_EQ(0, TestUtil::stopCountingNodes());
Persistent Yoga Summary: This is meant to show a possible route format for a persistent form of Yoga. Where previous layouts can remain intact while still taking advantage of incremental layout by reusing previous subtrees. ```c YGNodeRef YGNodeClone(const YGNodeRef node); ``` The core of this functionality is a new API to clone an existing node. This makes a new detached node with all the same values as the previous one. Conceptually this makes the original node "frozen" from that point on. It's now immutable. (This is not yet enforced at runtime in this PR but something we should add.) Since the original is frozen, we reuse the children set from the original node. Their parent pointers still point back to the original tree though. The cloned node is still mutable. It can have its styles updated, and nodes can be inserted or deleted. If an insertion/deletion happens on a cloned node whose children were reused, it'll first shallow clone its children automatically. As a convenience I also added an API to clear all children: ```c void YGNodeRemoveAllChildren(const YGNodeRef node); ``` During insert/delete, or as a result of layout a set of reused children may need to be first cloned. A kind of copy-on-write. When that happens, the host may want to respond. E.g. by updating the `context` such as by cloning any wrapper objects and attaching them to the new node. ```c typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, YGNodeRef newNode, YGNodeRef parent, int childIndex); void YGConfigSetNodeClonedFunc(YGConfigRef config, YGNodeClonedFunc callback); ``` This PR doesn't change any existing semantics for trees that are not first cloned. It's possible for a single node to exist in two trees at once and be used by multiple threads. Therefore it's not safe to recursively free a whole tree when you use persistence. To solve this, any user of the library has to manually manage ref counting or tracing GC. E.g. by replicating the tree structure in a wrapper. In a follow up we could consider moving ref counting into Yoga. Closes https://github.com/facebook/yoga/pull/636 Reviewed By: emilsjolander Differential Revision: D5941921 Pulled By: sebmarkbage fbshipit-source-id: c8e93421824c112d09c4773bed4e3141b6491ccf
2017-10-17 01:02:04 -07:00
}
TEST(YogaTest, mixed_shared_and_owned_children) {
// Don't try this at home!
YGNodeRef root0 = YGNodeNew();
YGNodeRef root1 = YGNodeNew();
YGNodeRef root0_child0 = YGNodeNew();
YGNodeRef root0_child0_0 = YGNodeNew();
YGNodeInsertChild(root0, root0_child0, 0);
YGNodeInsertChild(root0_child0, root0_child0_0, 0);
YGNodeRef root1_child0 = YGNodeNew();
YGNodeRef root1_child2 = YGNodeNew();
YGNodeInsertChild(root1, root1_child0, 0);
YGNodeInsertChild(root1, root1_child2, 1);
C++ Cleanup 3/N: Reorganize YGNode (#1350) Summary: X-link: https://github.com/facebook/react-native/pull/39219 Pull Request resolved: https://github.com/facebook/yoga/pull/1350 X-link: https://github.com/facebook/react-native/pull/39170 ## This diff This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`). The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it. This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs. ## This stack The organization of the C++ internals of Yoga are in need of attention. 1. Some of the C++ internals are namespaced, but others not. 2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these) 2. Most of the files are in a flat hierarchy, except for event tracing in its own folder 3. Some files and functions begin with YG, others don’t 4. Some functions are uppercase, others are not 5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about 6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h) 7. There is no clear indication from file structure or type naming what is private vs not 8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers This stack does some much needed spring cleaning: 1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy 3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended 4. Utils files are split 5. Most C++ internals drop the YG prefix 6. Most C++ internal function names are all lower camel case 7. We start to split up Yoga.cpp 8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings 9. It is not possible to use private APIs without static casting handles to internal classes This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well. These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer. Changelog: [Internal] bypass-github-export-checks Reviewed By: shwanton Differential Revision: D48847258 fbshipit-source-id: fc560893533b55a5c2d52c37d8e9a59f7369f174
2023-08-30 19:57:16 -07:00
auto children = static_cast<yoga::Node*>(root1)->getChildren();
children.insert(children.begin() + 1, static_cast<yoga::Node*>(root0_child0));
static_cast<yoga::Node*>(root1)->setChildren(children);
auto secondChild = YGNodeGetChild(root1, 1);
ASSERT_EQ(secondChild, YGNodeGetChild(root0, 0));
ASSERT_EQ(YGNodeGetChild(secondChild, 0), YGNodeGetChild(root0_child0, 0));
YGNodeCalculateLayout(root1, YGUndefined, YGUndefined, YGDirectionLTR);
secondChild = YGNodeGetChild(root1, 1);
ASSERT_NE(secondChild, YGNodeGetChild(root0, 0));
ASSERT_EQ(YGNodeGetOwner(secondChild), root1);
ASSERT_NE(YGNodeGetChild(secondChild, 0), YGNodeGetChild(root0_child0, 0));
ASSERT_EQ(YGNodeGetOwner(YGNodeGetChild(secondChild, 0)), secondChild);
}