Commit Graph

76 Commits

Author SHA1 Message Date
Joe Vilches
28975a6053 Remove public APIs for YGNodePrint and YGConfigSetPrintTreeFlag (#1567)
Summary:
X-link: https://github.com/facebook/react-native/pull/42688

Pull Request resolved: https://github.com/facebook/yoga/pull/1567

We are planning on overhauling NodeToString to output JSON instead of HTML for the purposes of better benchmarking and capturing trees in JSON format to benchmark later. This gives us a bit of a headache as we have to revise several build files to ensure this new library works, ensure that it is only included in certain debug builds, and deal with the benchmark <-> internal cross boundary that arises as the benchmark code (which is a separate binary) tries to interact with it.

On top of it all this is really not used at all.

The plan is to rip out this functionality and just put it in a separate binary that one can include if they really want to debug. That means that it cannot exist in the public API, so I am removing it here.

Private internals come next

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D53137544

fbshipit-source-id: 7571d243b914cd9bf09ac2418d9a1b86d1bee64a
2024-02-02 15:44:23 -08:00
Joe Vilches
47e9f33eb4 Java bindings for setAlwaysFormsContainingBlock (#1540)
Summary:
X-link: https://github.com/facebook/react-native/pull/42192

Pull Request resolved: https://github.com/facebook/yoga/pull/1540

tsia

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D52608259

fbshipit-source-id: 647ec4e2fe180ace8d6b641e17cd610fa53fe845
2024-01-08 20:28:49 -08:00
Nick Gerleman
f992e63ac5 Implement method bindings for gap/row-gap/column-gap
Summary:
This adds method bindings for `YGNodeStyleSetGap` and `YGNodeStyleGetGap`.

Changelog:
[Genral][Added] - Implement method bindings for yoga gap/row-gap/column-gap

Reviewed By: yungsters

Differential Revision: D39922411

fbshipit-source-id: 6cbb4d352203d2ec92df162c3f2f2efd02bd9568
2022-10-13 08:18:49 -07:00
Andres Suarez
acbffc8485 Update copyright headers from Facebook to Meta
Reviewed By: aaronabramov

Differential Revision: D33367752

fbshipit-source-id: 4ce94d184485e5ee0a62cf67ad2d3ba16e285c8f
2021-12-30 15:11:00 -08:00
Aditya Sharat
cbf6495d66 Refactor YogaNode.Inputs freeze API
Summary:
`InternalNode` will eventually not have a pointer to its parent. This diff removes one of the usages of the `InternalNode#getParent()` API. `InternalNode` will also not host the `YogaNode` eventually; so this diff also removes one of the usages of the `InternalNode#getYogaNode()` api.

Now the `Inputs#freeze` api will pass the parent's `YogaNode` and the `YogaNode` of the node (this) being measured.

Changelog: [Internal] Passes The YogaNode and parent YogaNode in the Inputs.freeze API

Reviewed By: SidharthGuglani

Differential Revision: D27240229

fbshipit-source-id: efc4ec3249a963c3181111f9b989d8ed9e17feb4
2021-03-30 05:42:09 -07:00
Aditya Sharat
07eaeea7a4 Create YogaProps Interface
Summary:
Create YogaProps Interface; this interface represents the inputs to YogaNode for layout calculation.

Changelog: [Internal] Create YogaProps Interface; this interface represents the inputs to YogaNode for layout calculation.

Reviewed By: mihaelao

Differential Revision: D27229274

fbshipit-source-id: 5205caf2384661369d7a2d7e7f3e49ff831a1c92
2021-03-26 10:05:14 -07:00
Aditya Sharat
de36e85004 Adds InternalNode#freeze() API
Summary:
Litho needs a new API which is called immediately before yoga begins layout calculations so that the InternalNode gets the opportunity to finalise itself; i.e. perform the last mutations and in effect avoid any more mutations to the hierarchy.

See D26373731 where the mutations from `Layout#collectResults` is moved back into the InternalNode.

Changelog: [Internal]  Adds new API to YogaNodeJNIBase

Reviewed By: SidharthGuglani

Differential Revision: D26373730

fbshipit-source-id: 471346d3444986ada91e86c95f5f9fb98bcd2fa6
2021-02-16 10:19:23 -08:00
Pasquale Anatriello
e983c4a5ef Swap child Yoga
Summary:
Changelog: [Internal]

Expose the replaceChild Yoga call to Java

Reviewed By: SidharthGuglani

Differential Revision: D19497193

fbshipit-source-id: 153243cc1d8c23dcaf2c772ca794bd59a230f652
2020-01-30 04:32:20 -08:00
Sidharth Guglani
27f42c90db Remove setStyleInputs API
Summary:
setStyleInputs batching API was added to reduce the number of jni calls and although it improved performance in yoga world but was not impactful in litho and is not used anywhere.

Removing this saves around 500 bytes per architecture

#Changelog:
[Internal][Yoga] Removed unused code setStyleInputs batching API form Yoga

Reviewed By: amir-shalem

Differential Revision: D18036536

fbshipit-source-id: 7436b55dcd464dd9f9cc46406d4fd78d12babe55
2019-10-23 02:38:03 -07:00
Andres Suarez
42bba10894 Tidy up license headers
Summary: Changelog: Tidy up license headers

Reviewed By: SidharthGuglani

Differential Revision: D17919414

fbshipit-source-id: 0501b495dc0a42256ca6ba3284a873da1ab175c0
2019-10-15 10:36:38 -07:00
Amir Shalem
96eb94afd0 Remove YogaNode.create() from the abstract class
Summary:
Remove YogaNode.create() from the abstract class after we made sure nothing uses it anymore
This is the final stage to make `YogaNode` a pure class without JNI references

Reviewed By: SidharthGuglani

Differential Revision: D17280571

fbshipit-source-id: bd0eb138f7a6a9de8988fc0a7b90badbf635dac5
2019-09-18 00:40:27 -07:00
David Aurelio
8c0eed3c75 Add PhantomRef based YogaNode subclass
Summary: Adds a subclass of `YogaNodeJNIBase` that uses `PhantomReference` for deallocating native memory rather than `Object#finalize()`. This should help making garbage collection more efficient.

Reviewed By: amir-shalem

Differential Revision: D16182667

fbshipit-source-id: d310fdb6af184168c43462b24f5e18ab5d0d7ad0
2019-07-19 17:24:49 -07:00
Sidharth Guglani
d676d917e3 moved all yoga node jni batching code to YogaNodeJNIBase and removed subclasses
Summary: Removed classes YogaNodeJNI and YogaNodeJNIBatching and all the logic have been moved to base class

Reviewed By: davidaurelio

Differential Revision: D16221484

fbshipit-source-id: 830819f5bc6010291b8bc0c6d90897cea991909f
2019-07-15 11:16:38 -07:00
Sidharth Guglani
838fc3f019 remove useBatchingForLayoutOutputs config param and start using batching for layout outputs
Summary: Removes config param useBatchingForLayoutOutputs and now we are using batching of layout outputs as float array while passing data from c++ to java

Reviewed By: davidaurelio

Differential Revision: D16221483

fbshipit-source-id: 326c668d4dfd13b2cf031f98a84bfa50b1440513
2019-07-15 11:16:38 -07:00
Aditya Sharat
8ff13c922a Back out "[litho] Adds check to unset a YogaNode's parent during reconciliation."
Summary: Removes `unsetOwner` from Yoga. This was temporarily for patching a crash.

Reviewed By: colriot

Differential Revision: D15737613

fbshipit-source-id: 8ab93ecf7ffb913df6207fe5db47a8cc93eded2c
2019-06-10 06:57:05 -07:00
Aditya Sharat
e33123f955 Adds check to unset a YogaNode's parent during reconciliation.
Summary: Adds check to unset a YogaNode's parent during reconciliation.

Reviewed By: davidaurelio

Differential Revision: D15714899

fbshipit-source-id: 6e2c2a28106574d062fec722c9a051acea87d0b6
2019-06-07 09:28:24 -07:00
Aditya Sharat
a1e47e37ae Add YogaNode.cloneWithoutChildren
Summary: Adding flat clone method back to YogaNode for reconciliation.

Reviewed By: davidaurelio

Differential Revision: D14683019

fbshipit-source-id: 08ed2ffbb16052cb11aa464f67a7ba9a64297985
2019-04-03 11:34:06 -07:00
Sidharth Guglani
74ce5afd9e added flag for useBatchingForLayoutOutputs experiment
Summary:
Using a config flag to switch between different implementations of transferring layout outputs
- YogaNodeJNI uses multiple access of java fields to pass all properties like width, height, margin etc...
- YogaNodeJNIBatching uses a float array to pass all the data in one java field access

Reviewed By: davidaurelio

Differential Revision: D14378301

fbshipit-source-id: 0da5b28e6a67ad8fd60eb7efe622d9b2deaf177f
2019-04-01 06:17:20 -07:00
Sidharth Guglani
5fad8008ab added test for reset method in YogaNodeJNI to verify all layout outputs are reset properly
Summary: This diff adds a test for reset method in YogaNodeJNI to verify all layout outputs are reset properly

Reviewed By: davidaurelio

Differential Revision: D14643926

fbshipit-source-id: fffbcd07ccb6d2df83fc0bf187d992ef194f3bd0
2019-04-01 06:17:19 -07:00
Sidharth Guglani
a2ee0f65bd fixed @Nullable missing infer errors
Summary: Fixed Nullable lint errors

Reviewed By: davidaurelio

Differential Revision: D14383930

fbshipit-source-id: 0847e19c50a0b8d4039199045b74aa86c96ac096
2019-03-25 06:36:28 -07:00
Sidharth Guglani
f273e80c77 created a new class YogaNodeJNI which extends YogaNodeJNIBase and using YogaNodeJNi for new object creation
Summary:
Added a child class of YogaNodeJNIBase which will be used to separate layout outputs transfer logic.
This change set is for adding experiment for layout outputs batching using a float array

Reviewed By: davidaurelio

Differential Revision: D14368098

fbshipit-source-id: e0f10fb61cd09ee47cf9ce41fb400f4cfb3dd795
2019-03-20 08:57:13 -07:00
Sidharth Guglani
f039835249 renamed YogaNodeJNI to YogaNodeJNIBase
Summary:
Renamed class from YogaNodeJNI to YogaNodeJNIBase.
This change set is for adding experiment for layout outputs batching using a float array where we will have two separate classes which will override how layout outputs are transferred to java YogaNode object.

We needed two separate classes because having everything in one class was causing memory issues as both the individual fields for width, height etc. and float array for batching needs to be present in code.

Reviewed By: davidaurelio

Differential Revision: D14368069

fbshipit-source-id: 0e98e28c8c7a9788345ccb92b2cd0f2cd4a53525
2019-03-20 07:53:48 -07:00
Sidharth Guglani
2abd9dd91b Added getters for flexWrap and flex style inputs in java bindings
Summary: Added getters fro flexWrap an flex style input properties in java bindings

Reviewed By: davidaurelio

Differential Revision: D14207517

fbshipit-source-id: e8d2ad17b5edbdf02e69011c6d95a4de076bd366
2019-02-28 12:52:01 -08:00
Sidharth Guglani
afadc5cf6b JNI batching API using float array
Summary:
Added a new API `YGNodeSetLayoutStyleInputs` to pass layout style inputs from java to native code.
All the style inputs are passed in a float array in [key1, key2, value2, key3, value3a, value3b .....] format over JNI layer.
There are three types of style inputs
- do not need any value to be passed along with them like WidthAuto, HeightAuto
- need one value to be passed like Width, Height
- need two values to be passed like Margin, Padding (edge value and actual margin, padding value)

Reviewed By: davidaurelio

Differential Revision: D14166948

fbshipit-source-id: 4bea64d6a429959c3962c87e337914dcd99199fd
2019-02-28 12:52:01 -08:00
David Aurelio
2643b96589 Extract abstract class YogaNode
Summary:
@public

Here, we extract an abstract class from `YogaNode`, in order to allow for experimentation with different implementations.

The reason for not choosing an interface is to keep ABI compatibility for `YogaNode.create()`.

Reviewed By: pasqualeanatriello

Differential Revision: D14044990

fbshipit-source-id: f8eb246338b55f34f0401198c0655abfcb7c9f37
2019-02-19 11:19:10 -08:00
David Aurelio
5a6d84abc9 Add YogaNode.create()
Summary:
Adds a factory method to `YogaNode`.

While this is purely redundant at the moment, it will allow experimentation in follow-up diffs. We will have concrete implementations deriving from `YogaNode` (which will be abstract).

Going through `YogaNode.create()` means that we can maintain ABI compatibility.

Reviewed By: amir-shalem

Differential Revision: D14122974

fbshipit-source-id: 15d92f296d91cc8bbd79a196f370d2dbb69b3f92
2019-02-18 03:08:14 -08:00
David Aurelio
5a9f29debb Delete functionality for shared childen
Summary:
@public

Removes `YGNodeInsertSharedChild` / `addSharedChildAt`.

This functionality is unused, and can cause memory leaks.

Reviewed By: SidharthGuglani

Differential Revision: D13711105

fbshipit-source-id: 86206c05393b3f1a497e6b046006f94ead88c6ce
2019-01-24 06:33:06 -08:00
David Aurelio
339c5574b8 Dealloc JNI implementation experiment
Summary:
@public

Remove ability to configure Yoga to run with/without JNI fast calls on dalvik / art.
This switches to always run with fast calls.

Reviewed By: astreet

Differential Revision: D13144652

fbshipit-source-id: 091aab0cd1290d46346323d3e26a11dd0bb17187
2018-11-22 04:01:06 -08:00
David Aurelio
7d7d9a7ef0 Back to yearless format for MIT license
Summary:
@public

Restores the yearless format of the MIT license.

Reviewed By: SidharthGuglani

Differential Revision: D13082510

fbshipit-source-id: f5a849b06652cedf68547d4a7963398b2627915f
2018-11-15 08:28:16 -08:00
Sidharth Guglani
4e2011c381 added functionality using which child node can tell parent node to use it as a reference baseline
Summary:
@public
Currently only parent can tell the layout to align its children based on baseline. But if one of the children is a column or row then basealign does not work as expected.

We have added an api setReferenceBaseline which when set to true would mean that it's baseline would be considered as the reference baseline for parent amongst its siblings. If there are more than one siblings with referenceBaseline set, the first one would be considered.

Reviewed By: davidaurelio

Differential Revision: D12883323

fbshipit-source-id: 19beccfc47d98bb38f81f5b66ba764e83680f821
2018-11-14 02:52:20 -08:00
Emily Janzer
36e732aabd Update copyright for yoga files
Summary: Appeasing lint

Reviewed By: hramos

Differential Revision: D9819535

fbshipit-source-id: 0080f17a62291291a0ea90942af4b38588e9057f
2018-10-05 18:46:14 -07:00
David Aurelio
6e46eec58f Adapt methods for fast calls
Summary:
Changes all possible native JNI methods to critical methods.

For now, this only changes Android up and including v7. In order to be prepared for the `CriticalNative` annotation in Android v8, the following restrictions apply:

- Qualifying methods must be static (this is also enforced for Dalvik / Android v4)
- Method implementations can only consume primitive JNI types (`boolean`, jchar`, etc.)

Reviewed By: priteshrnandgaonkar

Differential Revision: D9943868

fbshipit-source-id: 728817eb37822b717fd3daf94cd9f02b42c17db6
2018-09-25 16:03:16 -07:00
David Aurelio
554d8cdf76 Make native method binding configurable
Summary:
Moves binding of native methods into a separate native method that can be parameterized.

This will be used to experiment with JNI-related technology.

Reviewed By: priteshrnandgaonkar

Differential Revision: D9943870

fbshipit-source-id: 661f15537d5bbf7a3eef7717e3d99fed2de23904
2018-09-25 16:03:15 -07:00
Héctor Ramos
e224a29d1c Update copyright headers to yearless format
Summary: This change drops the year from the copyright headers and the LICENSE file.

Reviewed By: yungsters

Differential Revision: D9727774

fbshipit-source-id: df4fc1e4390733fe774b1a160dd41b4a3d83302a
2018-09-11 15:53:35 -07:00
David Aurelio
78cdf3cadc Back to JNI storage
Summary:
@public

This reverts the Yoga/Java storage experiment. I will follow up with any learnings.

Reviewed By: pasqualeanatriello

Differential Revision: D9168405

fbshipit-source-id: fb227fb9353bd4c4e3bebbe9b04eec1132e532e8
2018-08-06 02:12:40 -07:00
David Aurelio
be78bfbd8c Add Unsafe based storage backend
Summary:
@public

Adds another version of property storage for `YogaNode`, using `sun.misc.Unsafe`.

Adopts the stub concept from Litho for `Unsafe`, as it is hidden by the Android SDK.

Reviewed By: pasqualeanatriello

Differential Revision: D9140103

fbshipit-source-id: a4b376eca341b724a00f873467ae8bf8eaac69f4
2018-08-05 16:45:31 -07:00
David Aurelio
0e99980206 Adapt micro benchmark
Summary:
- adds more property assignments
- reduces the number of layout roots that exist simultanously

Reviewed By: pasqualeanatriello

Differential Revision: D8989389

fbshipit-source-id: 6a0ac800a4caad61a2f4bf98caa314855b70875f
2018-08-02 04:00:50 -07:00
David Aurelio
78d6988461 Add YogaNodeProperties implementation with ByteBuffer based setters
Summary:
@public
Adds an implementation of `YogaNodeProperties` that sets style properties using a `ByteBuffer` rather than JNI calls.
We hope for a speed improvement.

Reviewed By: pasqualeanatriello

Differential Revision: D9042225

fbshipit-source-id: c7f2b24eaeddd1190755bec85a5034079bd2f492
2018-07-30 09:43:37 -07:00
David Aurelio
3499e2e0ef Add YogaNodeProperties implementation based on ByteBuffer
Summary:
@public
Adds an implementation of `YogaNodeProperties` that accesses style and layout properties using a `ByteBuffer` rather than JNI calls.
We hope for a speed improvement.

This needs further cleanup after experimenting, e.g. to codegen the offsets.

Reviewed By: pasqualeanatriello

Differential Revision: D8911723

fbshipit-source-id: 3c24b57eb545155878896ebb5d64d4553eb6bedc
2018-07-30 09:43:37 -07:00
David Aurelio
b1821ab4cd move property storage into sub-object
Summary:
Here we introduce an abstraction over node property storage, in order to experiment with different approaches for Java/C integration.

- interface `YogaNodeProperties` as abstraction
- current JNI code factored into `YogaNodePropertiesJNI.java`
- `YogaNode` delegates all calls, no API changes

Reviewed By: astreet

Differential Revision: D8769448

fbshipit-source-id: e67327ce41fa047a51a986c652b3d59992a510e2
2018-07-30 09:43:37 -07:00
David Vacca
f4d29e6f11 Avoid cleaning up Owner of YGNode during clonning
Summary:
This diff refactors the cloning mechanism for YogaNode used from Fabric UI renderer and RN iOS graphs.
Previously, we were cleaning the owner of the child's cloned node inside the C++ implementation of YogaNode. This was a mistake because this modified the last commited YogaTree, causing side effect in RN iOS graphs.

Reviewed By: shergin

Differential Revision: D8672627

fbshipit-source-id: c9902d00690e0361fd58aed84b506c42258bd995
2018-06-28 22:47:50 -07:00
David Vacca
cdb1ee21a0 Clean-up parent / owner reference of children during clonning
Summary: This diff cleans up the parent / owner references for children of ReactShadowNode / YogaNode during cloning. The reason of this behavior is to avoid retaining every generation of trees during cloning. This fixes a memory leak detected when running the ProgressBarExample.android.js in catalyst app

Reviewed By: fkgozali

Differential Revision: D8019894

fbshipit-source-id: b0d38f0c836ffec534f64fa1adbd7511ecf3473d
2018-06-22 11:44:14 -07:00
Vince Mi
526ca42d04 Add back deprecated getParent methods for non-breaking API change (#775)
Summary:
I'm not totally sure what I'm doing so if this needs changes let me know.
Closes https://github.com/facebook/yoga/pull/775

Reviewed By: emilsjolander

Differential Revision: D8331892

Pulled By: passy

fbshipit-source-id: eb1023e666322d2472e4081fd4a4e72a7b43d049
2018-06-11 04:12:26 -07:00
Pascal Hartig
8e4f3a3084 Add getParent() method for easier migration
Summary:
External partners have asked us to provide an easier way to migrate and this was
a breaking change in the most recent release.

Reviewed By: emilsjolander

Differential Revision: D7654526

fbshipit-source-id: efe44807caa97f495c5e5691dedcf281760fa23e
2018-04-18 04:45:12 -07:00
Pritesh Nandgaonkar
9550126f76 clean up experimental setup and keep using ffast-math flag
Summary: Remove experimental setup of fast-math and add `-ffast-math` as a compiler flag for yoga.

Reviewed By: emilsjolander

Differential Revision: D7414228

fbshipit-source-id: 320e1b1953f6af867b13e617af7872eb8999160e
2018-04-04 08:29:43 -07:00
David Vacca
5e3ffb39a2 Extend Yoga to be able clone Yoga Node with new children
Summary:
This diff includes the following changes:

1 ) I extended the Java implementation of YogaNode to be able to get a full copy of a YogaNode object without copying the List of children of the original YogaNode. In other words, the new copy of the YogaNode will have the same data of the original YogaNode, but with an empty list of children.

2 ) We created a new method in Yoga.cpp called YGNodeInsertSharedChild. This new method is going to be used by Fabric in order to temporarily share a YogaNode between two "Yoga Trees" (the "current Yoga" tree and a partial "clone of the current Yoga tree"). We exposed this new functionality in the java implementation of Yoga (method addSharedChildAt)

I'm including sebmarkbage for more context.

Reviewed By: emilsjolander

Differential Revision: D7245421

fbshipit-source-id: 72578c8261f29e4a12fc6c72a91f2f891cd58d48
2018-04-01 18:35:13 -07:00
David Vacca
f0edefdbb7 Rename YogaNode.parent -> YogaNode.owner
Summary:
In the persistent version of Yoga, a YogaNode can be shared between two YogaTrees, that means that a YogaNode could have more than one Parent at one point in time. That's why the concept of Parent of a YogaNode is not a 1-1 relationship anymore.
This diff changes the semantic of Parent of a YogaNode to Owner of a Yoga Node. CC sebmarkbage and priteshrnandgaonkar for more context.

Technically this diff renames the field YogaNode.parent to YogaNode.owner (and every internal field, Getter and Setter that is related to parent)

Note that as part of this diff I also modified the CSSLayoutDEPRECATED version of Yoga in order to keep compatibility with the C++ implementation.

Reviewed By: priteshrnandgaonkar

Differential Revision: D7352778

fbshipit-source-id: dcf1af5e72bfc3063b5c4bda197d7952a9194768
2018-04-01 18:35:13 -07:00
David Vacca
17901ea5c2 Refactor cloning of YogaNode
Summary: see Test Plan

Reviewed By: priteshrnandgaonkar

Differential Revision: D7339832

fbshipit-source-id: 2de6f47ae7601ac083d3b9fbe10ffaf6307ae760
2018-04-01 18:35:13 -07:00
Pritesh Nandgaonkar
3dfb68887d Expose layout diffing flag to java
Summary:
This diff exposes `shouldDiffLayoutWithoutLegacyStretchBehaviour` from YGConfig and `doesLegacyStretchFlagAffectsLayout` from YGLayout to YogaConfig and YogaNode respectively

Also added a positive test case. Didn't find the negative test case example. @[508947467:ianc] or @[759512522:emilsj], can you suggest a negative test case example.

Reviewed By: emilsjolander

Differential Revision: D7272067

fbshipit-source-id: e67e82eb057e4c7124904c715f9dca4dcfea21ea
2018-03-14 08:43:01 -07:00
Pritesh Nandgaonkar
3a82d2b1a8 Change NaN with large number
Summary:
Changed NaN with large number to support `-ffast-math` compiler flag.For  `-ffast-math` to work, all floating point numbers should be finite. Reason for not going with `FLT_MAX`, is that, it may cause number overflow during math operations. So thats why I opted for big number smaller than `FLT_MAX`.  Earlier we used NaN, while NaN is involved in comparision the comparision operator behaves differently, it always returns false. Also operators like, fmaxf,fminf etc. have wierd beahviours. This diff takes care of those things as far as possible, and all tests are passing.

Running ./instrumentation_tests/run instrumentation_tests/com/facebook/feed/ctacoalescing:ctacoalescing --class AttachmentCallToActionSelectorBenchmarkTest --benchmark --extra-arg iterations=100 shows the perf gain of 13-15%

Reviewed By: emilsjolander

Differential Revision: D6969537

fbshipit-source-id: bdc09eaf703e0d313ca65c25a4fb44c99203d9bf
2018-03-01 04:03:19 -08:00