Summary:
It's more useful and consistent now because:
- We print compound layout metrics from the correct storage (not from Yoga nodes);
- It works fro any kind of layout now (but we still have just one);
- It's much clear and straight-forward.
Reviewed By: fkgozali
Differential Revision: D7607422
fbshipit-source-id: 4c3cd2848e785a7f77c7f591e376d00c7c09ade9
Summary: Quite trivial. We need this to debug print LayoutMetrics.
Reviewed By: fkgozali
Differential Revision: D7607417
fbshipit-source-id: f4fa52ad04be9757545a8ac627db184f171288e5
Summary:
The previous approach simply didn't work. :(
As you might notice, in previous implementation of ViewShadowNode::cloneAndReplaceChild we used `ViewShadowNode` class for cloning `childNode`, and this is incorrect becasue `childNode` might be instance of any class.
Reviewed By: fkgozali
Differential Revision: D7595016
fbshipit-source-id: 2215414926f2f7a2e2fd05ca2d065f10d6d32b74
Summary: Should be more performant theoretically.
Reviewed By: mdvacca
Differential Revision: D7591713
fbshipit-source-id: 74141053f2b91cb621cc0d573f89f3454512c585
Summary:
The new interface of ComponentDescriptor makes ShadowNode creation/cloning process a bit more explicit:
Now customers (UIManager) must prepare Props object explicitly before creation or cloning.
Besides general clarity, we need this to prepare for a new virtual `ShadowNode::clone()` method which will serve "virtual constructor" role,
redirecting execution to concrete ComponentDescriptor instance.
Actually, the whole purpose of concrete ComponentDescriptor instance is serve "virtual constructor" role (and all this code should be "templated").
Reviewed By: mdvacca
Differential Revision: D7591714
fbshipit-source-id: 8793b3ef70ed7ae113efb36ad1eee20573360dc8
Summary: Initial attempt to make fabric stuffs built properly with CocoaPods + RNTesterPods project. This simply includes fabric libs to the build, it doesn't change any behavior or enable fabric runtime.
Reviewed By: shergin
Differential Revision: D7626038
fbshipit-source-id: 4a80e0066cffa4478bb442fa8aefeaee6ff56ddd
Summary:
Apparently, there is no point to return a reference from this method because the struct (std::shared_ptr) is allocated on stack.
The test is also updated.
allow-large-files
Reviewed By: fkgozali
Differential Revision: D7557749
fbshipit-source-id: aa74146322c6d340256752586f05fc672024038e
Summary:
The previous name conflicts with the method with same (but with different semantic) name in `ShadowNode` class.
That was bad idea to use same name especially because the different semantic.
Reviewed By: fkgozali
Differential Revision: D7554549
fbshipit-source-id: 0bccbaacd0812f8a26592b2008f15ddb5bc34ebc
Summary:
`enum class` types do not provide default conversion to integers and reguire use typename before value names.
This must prevent bugs like the previous one where we used `Undefined` as a number value.
Reviewed By: fkgozali
Differential Revision: D7554548
fbshipit-source-id: b19379aae48c9aebe03043e08cf3acc712da3cb8
Summary: `Undefined` is actually equal `0` (becasuse it is `LayoutDirection::Undefined`), whereas `kFloatUndefined` is a magic huge number.
Reviewed By: fkgozali
Differential Revision: D7554550
fbshipit-source-id: 3ea37f0b8b6a59b4b0e00d205b75cd7252247d03
Summary: Quite obvious. We have to check `sealable` flag of the child (not a parent) before mutating it.
Reviewed By: mdvacca
Differential Revision: D7526407
fbshipit-source-id: ce8e0d6446ff7eb23baee9c92f246d5e198fe377
Summary:
YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector recently was disabled because of change in Yoga (D7339832).
This diff brings is back.
Reviewed By: mdvacca
Differential Revision: D7526417
fbshipit-source-id: 5369d641bf1e118132cf742d2d243bf426c0ffdb
Summary: UIManager uses UIManagerDelegate to communicate about shadow tree changes to another parts of the system.
Reviewed By: fkgozali
Differential Revision: D7503484
fbshipit-source-id: 0afe0f0d6cad31fe2ee9d61235d02b379cfe8217
Summary:
We have to call shallowSourceNode() in all cases of cloning which were not caused by UIManager instructions,
otherwise the diffing alogorith might produce incorrect mutation instructions.
Reviewed By: mdvacca
Differential Revision: D7503383
fbshipit-source-id: b33e5c39b7ba8cbd0f925fd29b3af379441a40a4
Summary: We have to call `ensureunSealed()` only if the node was changed.
Reviewed By: mdvacca
Differential Revision: D7503388
fbshipit-source-id: a3d07d50fa983ef93c14fa771711fa783fdf4c12
Summary:
Previously we generated `removed` *or* `delete` instruction, but sometimes we have to generate both.
So, basically we did it wrong. :(
Reviewed By: mdvacca
Differential Revision: D7503386
fbshipit-source-id: 8ee476abd29f088f31dc776f6e6a68d5293fbb35
Summary:
The method replaces the current source node with its source node.
It's useful when we have to clone some node but don't want to change a source node pointer.
Reviewed By: fkgozali
Differential Revision: D7503384
fbshipit-source-id: 81ec64079c7e99cb9abdda2af10d85281a94e1b1
Summary:
Quite trivial.
Note that std::unordered_map's `operator[]` is not `const`, so we have to use `at` instead.
Reviewed By: mdvacca
Differential Revision: D7467799
fbshipit-source-id: df38b21dccee4b347f7c070600af0d52f38d6570
Summary:
The first and quite naive implementation of The Diffing algorithm.
The exact set of instructions, their semantic, order, amount, and excessiveness are still unclear.
The concept should be verified by comprehensive testing with working native views rendering layer.
Reviewed By: mdvacca
Differential Revision: D7467790
fbshipit-source-id: 08f2f646e058cac8a4b73bf7b148e2748633348d
Summary:
Motivation:
* We never should call `markDirtyAndPropogate()` during tree construction/mutation because it might affect trees in different thread/dimentions;
* In Fabric we basically always have to dirty nodes ourselves manually duting tree construction;
* In Fabric we don't have "scoped/limited" tree mutations which require recursive dirtying; any mutation is creation of the new tree instance;
* Default value of the `isDirty` flag is "false", so we have to change this right after creation of Yoga node (and after cloning).
Reviewed By: mdvacca
Differential Revision: D7467797
fbshipit-source-id: 2c9144271dceea6ba2b95173209b99b5d86fbd87
Summary:
The modern Concurent Yoga's concept is:
We have to set parent/owner reference as part of `appendChild` process only if the current reference to parent/owner is `null`.
The motivation:
* Null-parent indicates that this node was not attached to anything yet;
* So, in this case there is no any concurrent memory access because we always create and (first time) attach the node on same thread;
* Simmetrical parent-child relationship indicates that we don't need to (re)clone assosiated ShadowNode (nor Yoga node).
Reviewed By: mdvacca
Differential Revision: D7467791
fbshipit-source-id: 9a7f517380fde3bb00272de18fd5dc13edb52071
Summary: Previously we recreate a vector with pointers to child nodes every single time we modify the collection. That was okay but recently I realized that the we can simply make a copy of the vector one time during object construction and then mutate it freely.
Reviewed By: mdvacca
Differential Revision: D7467796
fbshipit-source-id: 660f1706a19ae5f07c34c509f411ce9d67b93b35
Summary:
Computed `layoutMetrics` are also considered as part of ViewShadowNode's value.
In the future we probably have to add something like `localData` and `imperativeCommands`.
We need all this for diffing algorithm and mointing phase.
Reviewed By: mdvacca
Differential Revision: D7467800
fbshipit-source-id: 8a0dcf1fd2f97dc501d6969cb0b0f6a2c6a648b4
Summary: Using methods of the base class instead of custom implementation.
Reviewed By: fkgozali
Differential Revision: D7467795
fbshipit-source-id: 4d168b72880f6900bf8b747e1d655c10140e0c79
Summary: We have to have getters for all props/fields.
Reviewed By: mdvacca
Differential Revision: D7467792
fbshipit-source-id: 1492aad2d3398e6c14e0e354047730cf91201175
Summary: Two additional types of instructions were added and now all of them have explicitly clear semantic.
Reviewed By: fkgozali
Differential Revision: D7467798
fbshipit-source-id: 83c0e774d56975be504aa3fe892035f5f724f809
Summary: Slightly new approach: Some non-const methods might not always mutate objects, so sometimes we should call `ensureUnsealed()` only inside conditional branches where we actually mutate an instance.
Reviewed By: fkgozali
Differential Revision: D7467793
fbshipit-source-id: 1b9f229cf6816e54e0df36699a571fdb612d3c3c
Summary: Trivial. Those nits prevent cause compilation errors in some configurations.
Reviewed By: fkgozali
Differential Revision: D7467794
fbshipit-source-id: cbda285748374fd941a0b1ca6718d702ca2d6d82
Summary: Test for equality will be used in ShadowNode Tree Diffing algorithm.
Reviewed By: fkgozali
Differential Revision: D7467802
fbshipit-source-id: 5383add9fc7d7e4a772ca16e70a54f7e0c36823a
Summary:
This PR fixes the compilation on MSVC. I moved the `YGConfig` creation to a C++ constructor.
Addionally it removes the "dot" notation on `YGValue`, I didn't want to change that type to a C++ constructor, because I think this will break the ABI.
Closes https://github.com/facebook/yoga/pull/746
Differential Revision: D7498141
Pulled By: emilsjolander
fbshipit-source-id: 5f5308ff838dcd803065785ddc08b2404524acb9
Summary:
This PR fixes a type mismatch on `endOfLineIndex` where it should be of type `uint32_t` while it is of type `float`
Additonally it fixes some `double` casting in the rounding methods.
Closes https://github.com/facebook/yoga/pull/745
Differential Revision: D7494519
Pulled By: emilsjolander
fbshipit-source-id: 30a86574ce163458a6888f61a902d0640c1874fb
Summary: Any change must be propagate upwards.
Reviewed By: mdvacca
Differential Revision: D7389058
fbshipit-source-id: 09c74640d0e9607d2e17bdd31d7ce69df8565f72
Summary: Trivial. We don't need this because we already have another virtual method.
Reviewed By: fkgozali
Differential Revision: D7388964
fbshipit-source-id: 5ea6eb33ece72796d8cde2cc4b12c1240447d22a
Summary: The Great Diffing algorithm is coming.
Reviewed By: fkgozali
Differential Revision: D7376528
fbshipit-source-id: bdfef69551980136cfd1717a11ae376d5eef126b
Summary:
First, LayoutableShadowNode::cloneAndReplaceChild() is now pure virtual. The default implementation was useless and confusing.
Second, cloneAndReplaceChild() is now fully implemented for ViewShadowNode, so fancy Yoga Concurrent layout *should* work now.
Reviewed By: mdvacca
Differential Revision: D7376352
fbshipit-source-id: 1199a37e64535c8592a2a5480e60139f61d02006
Summary: We replace yogaNode with newly creaded (by Yoga) one. So, we have to set up context.
Reviewed By: mdvacca
Differential Revision: D7376345
fbshipit-source-id: 7926a10e3f057fc385e7731c354827aeb8245760
Summary: Trivial. We don't use it, and it shouldn't be exist by desing.
Reviewed By: mdvacca
Differential Revision: D7376351
fbshipit-source-id: 22f03af2b3596c274a22bab1fab6d8af854a7374
Summary:
We use shource nodes only in the diffing alogorithm. It implies that we have strong pointers to those nodes in trees we compare against.
Using weak_ptr's allows to avoid memory leaks.
Reviewed By: mdvacca
Differential Revision: D7376348
fbshipit-source-id: 34e5f58f18a00475f6bcdfbea3996b41c84dff62
Summary:
Fixed minor issue:
* use double spaces instead of a tab character for indentation
* depth should increase by 1, not 2
Reviewed By: shergin
Differential Revision: D7332803
fbshipit-source-id: 74fda2c7a4be4f509270d3074a7d71a3d4d32fe4
Summary:
Updates the Yoga Podspec to work with Cocoa frameworks. Replicated a2's work over on Yoga https://github.com/facebook/yoga/pull/726
(Which should also get merged too please ❤️ )
Set it manually in my own project and tested 👍
Only https://github.com/facebook/yoga/pull/726
[IOS] [BUGFIX] [Yoga] - Fixes to yoga.podspec when integrating into an iOS app using Cocoa frameworks.
Closes https://github.com/facebook/react-native/pull/18492
Differential Revision: D7355907
Pulled By: hramos
fbshipit-source-id: 6986d2bc560a23bb7f0f20b80e88bb440079891a
Summary: We will need this later in the diffing alogrithm.
Reviewed By: fkgozali
Differential Revision: D7330337
fbshipit-source-id: 3da44a62e4d5f30deed28b18a5779544153244f3
Summary: `LTR` is actually a default value for `direction` here, because an `inherit` value makes no sense for YGLayout (because it's *computed* value by definition).
Reviewed By: fkgozali
Differential Revision: D7330335
fbshipit-source-id: b3c7736c104689f2296e150f0cf57d622483d537
Summary: It is sometimes useful to report soft/fatal errors from C++ (native) to the same RCTExceptionsManager that is already handling JS exceptions. `ICxxExceptionManager` is an approach to provide such access, which impl provided for ObjC++.
Reviewed By: shergin
Differential Revision: D7224944
fbshipit-source-id: 8c607226b67851d46f4c787f5b6e6c8cb6a1afea
Summary: LayoutableShadowNode defines a unified interface (and set of primitives) essential for laying out shadow nodes.
Reviewed By: fkgozali
Differential Revision: D7230668
fbshipit-source-id: d8c1772d4c3bd1f87c41f7240a39aecebf3696ae
Summary: Foundation clases for Fabric designed to be "const-first".
Reviewed By: fkgozali
Differential Revision: D7230672
fbshipit-source-id: 433acd35a7958d5d577358b0a306923f970e573f
Summary:
* Fixed semantic: all kinds of derivative instances lose `sealed` flag (which is expected);
* Using atomic<bool> for `sealed_` ivar.
Reviewed By: fkgozali
Differential Revision: D7230674
fbshipit-source-id: abe786610c20a45a0fabb9068120e24adeeeac7f
Summary:
`Sealable` class represents something which can be *sealed* (imperatively marked as immutable).
Authored by shergin
Reviewed By: shergin
Differential Revision: D7174883
fbshipit-source-id: 8b26ca5b1a5154953a099895778eab86228acc46
Summary:
This is a very first diff in a series of undefined length implementing React Native Shadow Tree infra in C++.
All Shadow Nodes, Props and etc. implements `DebugStringConvertible`.
Authored by shergin
Reviewed By: shergin
Differential Revision: D7163868
fbshipit-source-id: 9c001aa5bd0723f709a07b1833f512c51e3bec11
Summary:
Some files have crept into the repo with the old license header. These are usually from PRs that were opened prior to the re-licensing of the project.
Let the script run, prior to fixing the errant files. The script outputs the following:
```
PATENTS crept into some new files?
--- /dev/fd/63 2018-03-01 01:42:48.250153746 +0000
+++ /dev/fd/62 2018-03-01 01:42:48.250153746 +0000
@@ -1 +1,9 @@
+Libraries/NativeAnimation/Nodes/RCTTrackingAnimatedNode.h
+Libraries/NativeAnimation/Nodes/RCTTrackingAnimatedNode.m
+ReactAndroid/src/main/java/com/facebook/react/animated/TrackingAnimatedNode.java
+ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLetterSpacingSpan.java
+ReactCommon/yoga/yoga/YGLayout.cpp
+ReactCommon/yoga/yoga/YGLayout.h
+ReactCommon/yoga/yoga/YGStyle.cpp
+ReactCommon/yoga/yoga/YGStyle.h
scripts/circleci/check_license.sh
Exited with code 1
```
Fix the headers in these files and run the script again. No output, exit code 0.
Closes https://github.com/facebook/react-native/pull/18143
Reviewed By: sophiebits
Differential Revision: D7119356
Pulled By: hramos
fbshipit-source-id: d238e4d4a3ae320a2c8e625c2fa29690057a4814
Summary:
Includes React Native and its dependencies Fresco, Metro, and Yoga. Excludes samples/examples/docs.
find: ^(?:( *)|( *(?:[\*~#]|::))( )? *)?Copyright (?:\(c\) )?(\d{4})\b.+Facebook[\s\S]+?BSD[\s\S]+?(?:this source tree|the same directory)\.$
replace: $1$2$3Copyright (c) $4-present, Facebook, Inc.\n$2\n$1$2$3This source code is licensed under the MIT license found in the\n$1$2$3LICENSE file in the root directory of this source tree.
Reviewed By: TheSavior, yungsters
Differential Revision: D7007050
fbshipit-source-id: 37dd6bf0ffec0923bfc99c260bb330683f35553e
Summary:
When the following conditions are met, the main size become smaller by the margins in the main axis.
* The aspect ratio is defined
* The main size is not defined
* The cross size is defined
* The main margin is defined
This is because the main margin size is not included when calculating the main size from the aspect ratio.
Closes https://github.com/facebook/yoga/pull/715
Reviewed By: emilsjolander
Differential Revision: D6998988
Pulled By: priteshrnandgaonkar
fbshipit-source-id: f6f69c47ece17bd7c5e41517b96032bf0c149356
Summary:
I've talked to several major community users, and they're all ok with deleting this
code. There's several doc fixes which will make it easier for third
party developers which should land about the same time this will.
Also buried along with it is RCTJSCExecutor.
Reviewed By: javache
Differential Revision: D6880781
fbshipit-source-id: b4cb1143def6fd23a96290e478fa728adbedacd3
Summary: Trivial. Those lines were lost during rebasing of the original commit.
Reviewed By: priteshrnandgaonkar
Differential Revision: D6717696
fbshipit-source-id: a5dce25427c8977352b3ae7ea01e546a540e0c13
Summary:
Fix for android github CI.
Github CI doesn't work without this change.
CI passes.
[INTERNAL] Android CI fix.
Closes https://github.com/facebook/react-native/pull/17551
Differential Revision: D6707477
Pulled By: hramos
fbshipit-source-id: f98515234da3b90ee3233523bcd7cc67ed82c1b4
Summary:
Currently, we can dirty leaf nodes with `measure` function, we also can get `dirty` status for any node, but we cannot handle a moment when this change happen. This diff introduces a new call-back-manner handler for it.
We need this to plug Yoga inside and outside other layout systems without maintaining own dirty propagation infrastructure.
Consider using Yoga for flex-box layout in React Native where we can have deeply nested layout like `<View><Text><View><Text/></View></Text></View>` where all content of all <Text> nodes are laid out using native text/inline (not flex-box!) layout system. In this case, when some change dirties some deeply nested Yoga node, we have to propagate the dirty state down to outer one. Having this handler makes possible to wire up `on-dirty` handler on the root node and `setDirtied` for the leaf node.
Removing custom dirting mechanism from React Native should drastically simplify rendering layer and bring a huge performance win.
Reviewed By: emilsjolander
Differential Revision: D6597856
fbshipit-source-id: 6588cd712f9c1dede4af32f3d326f90103e48ff0
Summary:
Fix min/max percentage constraints on measured nodes. Currently we passed in the available size instead of the parent size.
Fixes#611
Closes https://github.com/facebook/yoga/pull/649
Reviewed By: priteshrnandgaonkar
Differential Revision: D6408019
Pulled By: emilsjolander
fbshipit-source-id: 18be5056dbc0dc179970ec231ab8b4e2cdba65c5
Summary:
Suppresses the unused warnings on ```YGDefaultLog```. It uses a more generic macro which simply casts the variable to ```(void)```. This is the simples approach to make mutiple different compilers happy. Fixes#650
Closes https://github.com/facebook/yoga/pull/651
Differential Revision: D6407999
Pulled By: emilsjolander
fbshipit-source-id: 19fd78dd8b84eafdbb48875dd003d506a98c4807
Summary:
If you have a measurable node and set ```marign-left: auto``` + ```align-item:stretch``` on it, it won't get measured and they get a width/height of ```-(nan)```. This change fixes that behaviour. Fixes#644.
Closes https://github.com/facebook/yoga/pull/645
Differential Revision: D6413512
Pulled By: emilsjolander
fbshipit-source-id: 755febeb33bb0d4520ca6b3c28d56ac333e4a14d
Summary:
Adds new ```space-evenly``` for ```justify-content```.
Also adds a typofix in one of the other justify-content tests.
Fixes#657
Closes https://github.com/facebook/yoga/pull/658
Differential Revision: D6407996
Pulled By: emilsjolander
fbshipit-source-id: cc837409e1345624b4bd72c31e25fe68dcb0f6a3
Summary:
This fixes shrinking of elements which are in a non stretch alignment, but keeps the legacy stretch behaviour in place. Additionally this adds a testcase for ```useLegacyStretchBehaviour```
Fixes#633
Closes https://github.com/facebook/yoga/pull/635
Differential Revision: D6408037
Pulled By: emilsjolander
fbshipit-source-id: 377ab0308dd3a75a148a0af31ab5eb3ffb5b5d83
Summary:
If we have a values already set to undefined and set it to undefined again, we invalidate the layout. This change takes this case into account and keeps the layout valid.
Fixes#630
Closes https://github.com/facebook/yoga/pull/648
Differential Revision: D6408013
Pulled By: emilsjolander
fbshipit-source-id: dc2a848d84d3de9f4650fac9e41d7c8169446406
Summary:
Expected to be able to call `YGNodeLayoutGetMargin(node, YGEdgeEnd)`, but instead, the program aborts with `"Cannot get layout properties of multi-edge shorthands"`.
This bug seems to incorrectly prevent properties from YGEdgeEnd for all Layout properties.
Closes https://github.com/facebook/yoga/pull/632
Differential Revision: D6408060
Pulled By: emilsjolander
fbshipit-source-id: 4ab3b2ffb2f1bb6fd3a27f780caf0123abcdb230
Summary:
The min/max inner width shouldn't take the margins into account.
Adds a test for both cases.
Fixes#664
Closes https://github.com/facebook/yoga/pull/665
Differential Revision: D6407982
Pulled By: emilsjolander
fbshipit-source-id: ffa549a06f802263e3b8488e90756aa3f722d52d
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
Summary:
<!--
Thank you for sending the PR! We appreciate you spending the time to work on these changes.
Help us understand your motivation by explaining why you decided to make this change.
You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html
Happy contributing!
-->
Cxx module code swallows c++ exception details with sarcastic comment let native developer figure it out.
Now instead of swallowing it, we print as much information as we can for different exception types.
Still not ideal but way more informative.
Have a crash in your c++ module and try to figure it out without this change.
Closes https://github.com/facebook/react-native/pull/16193
Differential Revision: D6040038
Pulled By: javache
fbshipit-source-id: 3fbe838383ca13395e21f74c9549474f6329cfeb