@apollohg/react-native-rich-text-editor 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +160 -0
- package/README.md +259 -0
- package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
- package/ReactNativeProseEditor.podspec +59 -0
- package/THIRD_PARTY_NOTICES.md +31155 -0
- package/android/build.gradle +188 -0
- package/android/consumer-rules.pro +18 -0
- package/android/expo/build.gradle +17 -0
- package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
- package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
- package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
- package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
- package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
- package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
- package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
- package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
- package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
- package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
- package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
- package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
- package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
- package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
- package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
- package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
- package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
- package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
- package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
- package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
- package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
- package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
- package/android/src/main/jni/CMakeLists.txt +85 -0
- package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
- package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
- package/android/src/main/jni/reactnativeproseeditor.h +6 -0
- package/android/src/main/res/values/attrs.xml +6 -0
- package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
- package/app.plugin.js +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
- package/dist/AtomHost.d.ts +17 -0
- package/dist/AtomHost.js +135 -0
- package/dist/DefaultAtomChip.d.ts +2 -0
- package/dist/DefaultAtomChip.js +22 -0
- package/dist/EditorAddon.d.ts +32 -0
- package/dist/EditorAddon.js +29 -0
- package/dist/EditorAddonNormalization.d.ts +6 -0
- package/dist/EditorAddonNormalization.js +48 -0
- package/dist/EditorMentionThemeNormalization.d.ts +8 -0
- package/dist/EditorMentionThemeNormalization.js +22 -0
- package/dist/EditorMentionThemeValidation.d.ts +12 -0
- package/dist/EditorMentionThemeValidation.js +95 -0
- package/dist/EditorStyleSheet.d.ts +4 -0
- package/dist/EditorStyleSheet.js +10 -0
- package/dist/EditorStyleSheetNormalization.d.ts +3 -0
- package/dist/EditorStyleSheetNormalization.js +226 -0
- package/dist/EditorStyleSheetTypes.d.ts +151 -0
- package/dist/EditorStyleSheetTypes.js +2 -0
- package/dist/EditorTheme.d.ts +117 -0
- package/dist/EditorTheme.js +41 -0
- package/dist/EditorToolbar.d.ts +4 -0
- package/dist/EditorToolbar.js +17 -0
- package/dist/EditorToolbarComponent.d.ts +13 -0
- package/dist/EditorToolbarComponent.js +24 -0
- package/dist/EditorToolbarItems.d.ts +9 -0
- package/dist/EditorToolbarItems.js +58 -0
- package/dist/EditorToolbarRegistry.d.ts +59 -0
- package/dist/EditorToolbarRegistry.js +175 -0
- package/dist/EditorToolbarTypes.d.ts +273 -0
- package/dist/EditorToolbarTypes.js +2 -0
- package/dist/EditorToolbarVisuals.d.ts +154 -0
- package/dist/EditorToolbarVisuals.js +228 -0
- package/dist/EditorUpdateRevision.d.ts +8 -0
- package/dist/EditorUpdateRevision.js +22 -0
- package/dist/ExternalTextComposition.d.ts +45 -0
- package/dist/ExternalTextComposition.js +537 -0
- package/dist/ImageLoadingPolicy.d.ts +54 -0
- package/dist/ImageLoadingPolicy.js +60 -0
- package/dist/NativeEditorBoundaryError.d.ts +111 -0
- package/dist/NativeEditorBoundaryError.js +268 -0
- package/dist/NativeEditorBridge.d.ts +9 -0
- package/dist/NativeEditorBridge.js +37 -0
- package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
- package/dist/NativeEditorCollaborationTransport.js +275 -0
- package/dist/NativeEditorCreateConfig.d.ts +16 -0
- package/dist/NativeEditorCreateConfig.js +249 -0
- package/dist/NativeEditorCreateJson.d.ts +76 -0
- package/dist/NativeEditorCreateJson.js +474 -0
- package/dist/NativeEditorCreateValidation.d.ts +32 -0
- package/dist/NativeEditorCreateValidation.js +150 -0
- package/dist/NativeEditorDocumentBridge.d.ts +72 -0
- package/dist/NativeEditorDocumentBridge.js +305 -0
- package/dist/NativeEditorDocumentHandle.d.ts +70 -0
- package/dist/NativeEditorDocumentHandle.js +89 -0
- package/dist/NativeEditorLocalAwareness.d.ts +34 -0
- package/dist/NativeEditorLocalAwareness.js +153 -0
- package/dist/NativeEditorNativeModule.d.ts +48 -0
- package/dist/NativeEditorNativeModule.js +34 -0
- package/dist/NativeEditorRenderNormalization.d.ts +22 -0
- package/dist/NativeEditorRenderNormalization.js +274 -0
- package/dist/NativeEditorResultNormalization.d.ts +108 -0
- package/dist/NativeEditorResultNormalization.js +400 -0
- package/dist/NativeEditorTypes.d.ts +318 -0
- package/dist/NativeEditorTypes.js +15 -0
- package/dist/NativeEditorV2Decimal.d.ts +5 -0
- package/dist/NativeEditorV2Decimal.js +18 -0
- package/dist/NativeProseViewer.d.ts +122 -0
- package/dist/NativeProseViewer.js +130 -0
- package/dist/NativeRichTextEditor.d.ts +3 -0
- package/dist/NativeRichTextEditor.js +6 -0
- package/dist/ResourceLimits.d.ts +100 -0
- package/dist/ResourceLimits.js +80 -0
- package/dist/RichTextEditor.d.ts +4 -0
- package/dist/RichTextEditor.js +25 -0
- package/dist/RichTextEditorNativeTypes.d.ts +122 -0
- package/dist/RichTextEditorNativeTypes.js +2 -0
- package/dist/RichTextEditorNativeView.d.ts +12 -0
- package/dist/RichTextEditorNativeView.js +15 -0
- package/dist/RichTextEditorSerialization.d.ts +53 -0
- package/dist/RichTextEditorSerialization.js +283 -0
- package/dist/RichTextEditorTypes.d.ts +281 -0
- package/dist/RichTextEditorTypes.js +2 -0
- package/dist/ViewerConfiguration.d.ts +22 -0
- package/dist/ViewerConfiguration.js +6 -0
- package/dist/YjsCollaboration.d.ts +146 -0
- package/dist/YjsCollaboration.js +496 -0
- package/dist/addons.d.ts +193 -0
- package/dist/addons.js +122 -0
- package/dist/atomConstants.d.ts +3 -0
- package/dist/atomConstants.js +6 -0
- package/dist/atomInstances.d.ts +35 -0
- package/dist/atomInstances.js +79 -0
- package/dist/atomPolicy.d.ts +4 -0
- package/dist/atomPolicy.js +47 -0
- package/dist/atomUpdates.d.ts +2 -0
- package/dist/atomUpdates.js +38 -0
- package/dist/atoms.d.ts +92 -0
- package/dist/atoms.js +222 -0
- package/dist/attributeValidation.d.ts +4 -0
- package/dist/attributeValidation.js +97 -0
- package/dist/contentExpression.d.ts +6 -0
- package/dist/contentExpression.js +335 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +78 -0
- package/dist/listMarkers.d.ts +15 -0
- package/dist/listMarkers.js +21 -0
- package/dist/schemaDefinition.d.ts +143 -0
- package/dist/schemaDefinition.js +206 -0
- package/dist/schemaDocument.d.ts +17 -0
- package/dist/schemaDocument.js +69 -0
- package/dist/schemaDocumentConstruction.d.ts +5 -0
- package/dist/schemaDocumentConstruction.js +125 -0
- package/dist/schemaNormalization.d.ts +29 -0
- package/dist/schemaNormalization.js +313 -0
- package/dist/schemaPresets.d.ts +68 -0
- package/dist/schemaPresets.js +198 -0
- package/dist/schemaResolution.d.ts +9 -0
- package/dist/schemaResolution.js +211 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +28 -0
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
- package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
- package/dist/useEditorToolbarInteractions.d.ts +12 -0
- package/dist/useEditorToolbarInteractions.js +218 -0
- package/dist/useEditorToolbarItems.d.ts +8 -0
- package/dist/useEditorToolbarItems.js +280 -0
- package/dist/useEditorToolbarPresentation.d.ts +4 -0
- package/dist/useEditorToolbarPresentation.js +203 -0
- package/dist/useEditorToolbarState.d.ts +63 -0
- package/dist/useEditorToolbarState.js +108 -0
- package/dist/useFocusPreservingFrames.d.ts +17 -0
- package/dist/useFocusPreservingFrames.js +108 -0
- package/dist/useNativeEditor.d.ts +66 -0
- package/dist/useNativeEditor.js +370 -0
- package/dist/useRichTextEditorCommands.d.ts +21 -0
- package/dist/useRichTextEditorCommands.js +299 -0
- package/dist/useRichTextEditorEvents.d.ts +18 -0
- package/dist/useRichTextEditorEvents.js +216 -0
- package/dist/useRichTextEditorMentions.d.ts +11 -0
- package/dist/useRichTextEditorMentions.js +316 -0
- package/dist/useRichTextEditorPresentation.d.ts +5 -0
- package/dist/useRichTextEditorPresentation.js +178 -0
- package/dist/useRichTextEditorState.d.ts +180 -0
- package/dist/useRichTextEditorState.js +405 -0
- package/dist/useRichTextEditorUpdates.d.ts +10 -0
- package/dist/useRichTextEditorUpdates.js +308 -0
- package/dist/useViewerAtoms.d.ts +38 -0
- package/dist/useViewerAtoms.js +288 -0
- package/expo-module.config.json +13 -0
- package/ios/EditorAddons.swift +354 -0
- package/ios/EditorCodeHighlighting.swift +184 -0
- package/ios/EditorCore.xcframework/Info.plist +44 -0
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorInteractionOverlays.swift +697 -0
- package/ios/EditorLayoutManager.swift +971 -0
- package/ios/EditorNodeTypes.swift +37 -0
- package/ios/EditorStyleSheet.swift +461 -0
- package/ios/EditorTextView+AttributedPatch.swift +305 -0
- package/ios/EditorTextView+Binding.swift +61 -0
- package/ios/EditorTextView+Caret.swift +333 -0
- package/ios/EditorTextView+Commands.swift +203 -0
- package/ios/EditorTextView+Delegate.swift +65 -0
- package/ios/EditorTextView+Diagnostics.swift +225 -0
- package/ios/EditorTextView+DragDrop.swift +290 -0
- package/ios/EditorTextView+ExternalComposition.swift +364 -0
- package/ios/EditorTextView+Images.swift +354 -0
- package/ios/EditorTextView+InputTraits.swift +200 -0
- package/ios/EditorTextView+KeyboardInput.swift +318 -0
- package/ios/EditorTextView+Layout.swift +245 -0
- package/ios/EditorTextView+MarkedText.swift +267 -0
- package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
- package/ios/EditorTextView+NativeMutation.swift +471 -0
- package/ios/EditorTextView+Reconciliation.swift +97 -0
- package/ios/EditorTextView+RenderPatch.swift +573 -0
- package/ios/EditorTextView+Rendering.swift +539 -0
- package/ios/EditorTextView+Selection.swift +542 -0
- package/ios/EditorTextView.swift +953 -0
- package/ios/EditorTheme.swift +626 -0
- package/ios/EditorV2Adapter+Commands.swift +204 -0
- package/ios/EditorV2Adapter+Envelope.swift +238 -0
- package/ios/EditorV2Adapter+Mutation.swift +392 -0
- package/ios/EditorV2Adapter+Registry.swift +185 -0
- package/ios/EditorV2Adapter+RenderState.swift +360 -0
- package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
- package/ios/EditorV2Adapter+Selection.swift +329 -0
- package/ios/EditorV2Adapter+TextCommands.swift +430 -0
- package/ios/EditorV2Adapter.swift +376 -0
- package/ios/EditorV2Shadow.swift +353 -0
- package/ios/Generated_editor_core.swift +2378 -0
- package/ios/NativeCodeHighlighting.swift +159 -0
- package/ios/NativeCollaborationSocket.swift +144 -0
- package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
- package/ios/NativeCollaborationTransport+Socket.swift +379 -0
- package/ios/NativeCollaborationTransport+Validation.swift +103 -0
- package/ios/NativeCollaborationTransport.swift +322 -0
- package/ios/NativeCollaborationTransportRegistry.swift +521 -0
- package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
- package/ios/NativeEditorExpoView+Binding.swift +268 -0
- package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
- package/ios/NativeEditorExpoView+Events.swift +131 -0
- package/ios/NativeEditorExpoView+Focus.swift +169 -0
- package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
- package/ios/NativeEditorExpoView+Mentions.swift +288 -0
- package/ios/NativeEditorExpoView+Properties.swift +300 -0
- package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
- package/ios/NativeEditorExpoView+Updates.swift +312 -0
- package/ios/NativeEditorExpoView.swift +349 -0
- package/ios/NativeEditorModule.swift +856 -0
- package/ios/NativeEditorViewRegistry.swift +226 -0
- package/ios/NativeToolbar+ButtonStyle.swift +214 -0
- package/ios/NativeToolbar+Buttons.swift +228 -0
- package/ios/NativeToolbar+Diagnostics.swift +114 -0
- package/ios/NativeToolbar+Menu.swift +101 -0
- package/ios/NativeToolbar+Models.swift +519 -0
- package/ios/NativeToolbar+Placeholder.swift +39 -0
- package/ios/NativeToolbar.swift +612 -0
- package/ios/OrderedListMarkerFormatter.swift +103 -0
- package/ios/PositionBridge.swift +596 -0
- package/ios/ProseViewerView.swift +462 -0
- package/ios/RenderBridge+Atoms.swift +288 -0
- package/ios/RenderBridge+Attachments.swift +246 -0
- package/ios/RenderBridge+BlockBoundaries.swift +313 -0
- package/ios/RenderBridge+Style.swift +343 -0
- package/ios/RenderBridge.swift +704 -0
- package/ios/RichTextEditorView.swift +930 -0
- package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
- package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
- package/ios/SharedNativeImagePipeline.swift +533 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
- package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
- package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
- package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
- package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
- package/ios/Viewer/NativeSVGPreflight.h +11 -0
- package/ios/Viewer/NativeSVGPreflight.mm +219 -0
- package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
- package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
- package/ios/Viewer/PreparedProseLayout.swift +326 -0
- package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
- package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
- package/ios/Viewer/ViewerDocument.swift +575 -0
- package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
- package/ios/Viewer/ViewerImagePipeline.swift +478 -0
- package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
- package/ios/editor_coreFFI/module.modulemap +7 -0
- package/package.json +151 -0
- package/react-native.config.js +17 -0
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
- package/src/AtomHost.tsx +180 -0
- package/src/DefaultAtomChip.tsx +27 -0
- package/src/EditorAddon.ts +65 -0
- package/src/EditorAddonNormalization.ts +49 -0
- package/src/EditorMentionThemeNormalization.ts +25 -0
- package/src/EditorMentionThemeValidation.ts +133 -0
- package/src/EditorStyleSheet.ts +9 -0
- package/src/EditorStyleSheetNormalization.ts +219 -0
- package/src/EditorStyleSheetTypes.ts +189 -0
- package/src/EditorTheme.ts +196 -0
- package/src/EditorToolbar.tsx +31 -0
- package/src/EditorToolbarComponent.tsx +23 -0
- package/src/EditorToolbarItems.tsx +64 -0
- package/src/EditorToolbarRegistry.tsx +254 -0
- package/src/EditorToolbarTypes.tsx +322 -0
- package/src/EditorToolbarVisuals.tsx +270 -0
- package/src/EditorUpdateRevision.ts +26 -0
- package/src/ExternalTextComposition.ts +658 -0
- package/src/ImageLoadingPolicy.ts +125 -0
- package/src/NativeEditorBoundaryError.ts +345 -0
- package/src/NativeEditorBridge.ts +93 -0
- package/src/NativeEditorCollaborationTransport.ts +493 -0
- package/src/NativeEditorCreateConfig.ts +326 -0
- package/src/NativeEditorCreateJson.ts +579 -0
- package/src/NativeEditorCreateValidation.ts +178 -0
- package/src/NativeEditorDocumentBridge.ts +485 -0
- package/src/NativeEditorDocumentHandle.ts +168 -0
- package/src/NativeEditorLocalAwareness.ts +168 -0
- package/src/NativeEditorNativeModule.ts +97 -0
- package/src/NativeEditorRenderNormalization.ts +320 -0
- package/src/NativeEditorResultNormalization.ts +469 -0
- package/src/NativeEditorTypes.ts +366 -0
- package/src/NativeEditorV2Decimal.ts +13 -0
- package/src/NativeProseViewer.tsx +356 -0
- package/src/NativeRichTextEditor.tsx +33 -0
- package/src/ResourceLimits.ts +187 -0
- package/src/RichTextEditor.tsx +27 -0
- package/src/RichTextEditorNativeTypes.tsx +144 -0
- package/src/RichTextEditorNativeView.tsx +21 -0
- package/src/RichTextEditorSerialization.tsx +381 -0
- package/src/RichTextEditorTypes.tsx +327 -0
- package/src/ViewerConfiguration.ts +23 -0
- package/src/YjsCollaboration.ts +692 -0
- package/src/addons.ts +311 -0
- package/src/atomConstants.ts +3 -0
- package/src/atomInstances.ts +123 -0
- package/src/atomPolicy.ts +47 -0
- package/src/atomUpdates.ts +52 -0
- package/src/atoms.ts +379 -0
- package/src/attributeValidation.ts +116 -0
- package/src/contentExpression.ts +338 -0
- package/src/index.ts +295 -0
- package/src/listMarkers.ts +18 -0
- package/src/schemaDefinition.ts +395 -0
- package/src/schemaDocument.ts +92 -0
- package/src/schemaDocumentConstruction.ts +148 -0
- package/src/schemaNormalization.ts +367 -0
- package/src/schemaPresets.ts +250 -0
- package/src/schemaResolution.ts +242 -0
- package/src/schemas.ts +43 -0
- package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
- package/src/useEditorToolbarInteractions.tsx +329 -0
- package/src/useEditorToolbarItems.tsx +390 -0
- package/src/useEditorToolbarPresentation.tsx +483 -0
- package/src/useEditorToolbarState.tsx +172 -0
- package/src/useFocusPreservingFrames.ts +171 -0
- package/src/useNativeEditor.ts +534 -0
- package/src/useRichTextEditorCommands.tsx +478 -0
- package/src/useRichTextEditorEvents.tsx +342 -0
- package/src/useRichTextEditorMentions.tsx +441 -0
- package/src/useRichTextEditorPresentation.tsx +563 -0
- package/src/useRichTextEditorState.tsx +620 -0
- package/src/useRichTextEditorUpdates.tsx +403 -0
- package/src/useViewerAtoms.tsx +427 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Owns unmounted layouts and exact Yoga-to-Fabric handoffs. A lease shares
|
|
4
|
+
/// its immutable artifact with the LRU; lifecycle release retires any live
|
|
5
|
+
/// handoff before a later component can acquire it.
|
|
6
|
+
final class PreparedProseLayoutCache {
|
|
7
|
+
static let pixelGridRoundingSlackPixels = 1
|
|
8
|
+
|
|
9
|
+
private final class Preparation {
|
|
10
|
+
var result: Result<PreparedProseLayout, Error>?
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private let condition = NSCondition()
|
|
14
|
+
private var completed: [ProseLayoutKey: PreparedProseLayout] = [:]
|
|
15
|
+
/// Accesses append a generation token instead of moving an existing array
|
|
16
|
+
/// element. Stale tokens are ignored at eviction time and periodically
|
|
17
|
+
/// compacted, so ordinary cache hits remain O(1) while eviction retains
|
|
18
|
+
/// exact LRU order.
|
|
19
|
+
private var accessOrder: [(key: ProseLayoutKey, generation: UInt64)] = []
|
|
20
|
+
private var accessGenerations: [ProseLayoutKey: UInt64] = [:]
|
|
21
|
+
private var accessOrderHead = 0
|
|
22
|
+
private var nextAccessGeneration: UInt64 = 0
|
|
23
|
+
private var inFlight: [ProseLayoutKey: Preparation] = [:]
|
|
24
|
+
/// Yoga measurement hands an immutable artifact to Fabric before there is
|
|
25
|
+
/// a component view. A pending handoff is therefore distinct from a
|
|
26
|
+
/// mounted owner: mount consumes it exactly once. Both are live exact
|
|
27
|
+
/// owners and remain retained until their lifecycle releases them.
|
|
28
|
+
private var pendingLeases: [FabricLeaseKey: PreparedProseLayout] = [:]
|
|
29
|
+
private var mountedLeases: [FabricLeaseKey: PreparedProseLayout] = [:]
|
|
30
|
+
private var directMounted: [String: PreparedProseLayout] = [:]
|
|
31
|
+
/// Secondary ownership indexes make mount/release lookup proportional to
|
|
32
|
+
/// the affected owners, not every retained artifact.
|
|
33
|
+
private var pendingLeaseKeysByOwner: [FabricLeaseOwner: Set<FabricLeaseKey>] = [:]
|
|
34
|
+
private var mountedLeaseKeysByOwner: [FabricLeaseOwner: Set<FabricLeaseKey>] = [:]
|
|
35
|
+
private var pendingLeaseKeysBySurface: [FabricSurfaceToken: Set<FabricLeaseKey>] = [:]
|
|
36
|
+
private var mountedLeaseKeysBySurface: [FabricSurfaceToken: Set<FabricLeaseKey>] = [:]
|
|
37
|
+
private var pendingLeaseKeysByLayout: [ProseLayoutKey: Set<FabricLeaseKey>] = [:]
|
|
38
|
+
private var mountedLeaseKeysByLayout: [ProseLayoutKey: Set<FabricLeaseKey>] = [:]
|
|
39
|
+
private var directOwnersByLayout: [ProseLayoutKey: Set<String>] = [:]
|
|
40
|
+
|
|
41
|
+
private struct LayoutOwnership {
|
|
42
|
+
let layout: PreparedProseLayout
|
|
43
|
+
var completedReferences = 0
|
|
44
|
+
var pendingReferences = 0
|
|
45
|
+
var mountedReferences = 0
|
|
46
|
+
var directReferences = 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// All retention totals are maintained on identity transitions. A layout
|
|
50
|
+
/// may be referenced by several roles, but is counted once in each metric
|
|
51
|
+
/// with the same exclusions as the former set-based calculations.
|
|
52
|
+
private var ownershipByIdentifier: [ObjectIdentifier: LayoutOwnership] = [:]
|
|
53
|
+
private var retainedBytes = 0
|
|
54
|
+
private var budgetedRetainedBytes = 0
|
|
55
|
+
private var unmountedRetainedBytes = 0
|
|
56
|
+
private var fabricLeaseRetainedBytes = 0
|
|
57
|
+
private var directMountedRetainedBytes = 0
|
|
58
|
+
private var oversizedLeaseCount = 0
|
|
59
|
+
private var benchmarkCensusKeys: Set<ProseLayoutKey>?
|
|
60
|
+
private var mountIndex: [ProseMountKey: ProseLayoutKey] = [:]
|
|
61
|
+
#if DEBUG
|
|
62
|
+
/// A live generation must publish an artifact once for its complete
|
|
63
|
+
/// semantic/physical-width/revision key. Eviction retires only unmounted
|
|
64
|
+
/// entries, so a mounted owner can never be republished accidentally.
|
|
65
|
+
private var publishedKeys: Set<ProseLayoutKey> = []
|
|
66
|
+
private var livePublicationCounts: [ProseLayoutKey: Int] = [:]
|
|
67
|
+
private var publicationRetirementCandidates: Set<ProseLayoutKey> = []
|
|
68
|
+
#endif
|
|
69
|
+
private let byteBudget: Int
|
|
70
|
+
init(byteBudget: Int = 32 * 1024 * 1024) {
|
|
71
|
+
self.byteBudget = byteBudget
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func value(
|
|
75
|
+
for key: ProseLayoutKey,
|
|
76
|
+
fabricSurface: FabricSurfaceToken? = nil,
|
|
77
|
+
fabricLeaseHandle: UInt64? = nil,
|
|
78
|
+
shouldCreateFabricLease: (() -> Bool)? = nil,
|
|
79
|
+
build: () throws -> PreparedProseLayout
|
|
80
|
+
) throws -> PreparedProseLayout {
|
|
81
|
+
precondition(fabricSurface == nil || fabricLeaseHandle != nil)
|
|
82
|
+
let lookupStarted = PreparedProseInstrumentation.now()
|
|
83
|
+
condition.lock()
|
|
84
|
+
benchmarkCensusKeys?.insert(key)
|
|
85
|
+
// Mounted ownership is the most authoritative exact-key cache entry:
|
|
86
|
+
// it survives unmounted-cache eviction and memory warnings. Consult it
|
|
87
|
+
// before completed entries, because creating a same-width pending lease
|
|
88
|
+
// would otherwise prune a different-width replacement already pending
|
|
89
|
+
// for this Fabric surface and generation.
|
|
90
|
+
if let layout = mountedLayoutLocked(
|
|
91
|
+
for: key,
|
|
92
|
+
fabricSurface: fabricSurface,
|
|
93
|
+
fabricLeaseHandle: fabricLeaseHandle
|
|
94
|
+
) {
|
|
95
|
+
condition.unlock()
|
|
96
|
+
PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
|
|
97
|
+
return layout
|
|
98
|
+
}
|
|
99
|
+
if let layout = completed[key] {
|
|
100
|
+
touch(key)
|
|
101
|
+
if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
|
|
102
|
+
createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
|
|
103
|
+
}
|
|
104
|
+
condition.unlock()
|
|
105
|
+
PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
|
|
106
|
+
return layout
|
|
107
|
+
}
|
|
108
|
+
// Immutable prepared layouts are global to their complete layout key,
|
|
109
|
+
// while Fabric ownership is intentionally surface-scoped. Any live
|
|
110
|
+
// owner can therefore satisfy another caller without preparing a
|
|
111
|
+
// duplicate artifact. Fabric receives its own exact-once pending
|
|
112
|
+
// lease; UIKit simply reuses the immutable value without inventing a
|
|
113
|
+
// Fabric owner.
|
|
114
|
+
if let layout = liveLayoutLocked(for: key) {
|
|
115
|
+
if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
|
|
116
|
+
createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
|
|
117
|
+
}
|
|
118
|
+
condition.unlock()
|
|
119
|
+
PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
|
|
120
|
+
return layout
|
|
121
|
+
}
|
|
122
|
+
if let preparation = inFlight[key] {
|
|
123
|
+
while preparation.result == nil { condition.wait() }
|
|
124
|
+
let result = preparation.result!
|
|
125
|
+
if case let .success(layout) = result, let fabricSurface, let fabricLeaseHandle,
|
|
126
|
+
shouldCreateFabricLease?() ?? true {
|
|
127
|
+
createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
|
|
128
|
+
}
|
|
129
|
+
condition.unlock()
|
|
130
|
+
PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true, waited: true)
|
|
131
|
+
return try result.get()
|
|
132
|
+
}
|
|
133
|
+
let preparation = Preparation()
|
|
134
|
+
inFlight[key] = preparation
|
|
135
|
+
condition.unlock()
|
|
136
|
+
PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: false)
|
|
137
|
+
|
|
138
|
+
let result = Result(catching: build)
|
|
139
|
+
|
|
140
|
+
condition.lock()
|
|
141
|
+
if case let .success(layout) = result {
|
|
142
|
+
#if DEBUG
|
|
143
|
+
if !publishedKeys.insert(key).inserted {
|
|
144
|
+
PreparedProseInstrumentation.duplicatePublication()
|
|
145
|
+
preconditionFailure("Prepared prose layout published twice for a live semantic/width/revision key.")
|
|
146
|
+
}
|
|
147
|
+
// A build that is neither completed nor leased must retire before
|
|
148
|
+
// a later publication of the same key. Role insertion removes this
|
|
149
|
+
// candidate, so handoff transfers never transiently retire live
|
|
150
|
+
// publication state.
|
|
151
|
+
publicationRetirementCandidates.insert(key)
|
|
152
|
+
#endif
|
|
153
|
+
if layout.retainedBytes <= byteBudget {
|
|
154
|
+
insertCompletedLocked(layout, for: key)
|
|
155
|
+
}
|
|
156
|
+
if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
|
|
157
|
+
createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
|
|
158
|
+
} else {
|
|
159
|
+
enforceBudgetLocked()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
preparation.result = result
|
|
163
|
+
inFlight.removeValue(forKey: key)
|
|
164
|
+
condition.broadcast()
|
|
165
|
+
condition.unlock()
|
|
166
|
+
return try result.get()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Used only by Fabric mount. It never builds, compiles, or prepares.
|
|
170
|
+
func acquireForFabricMount(
|
|
171
|
+
surface: FabricSurfaceToken,
|
|
172
|
+
generationIdentity: String,
|
|
173
|
+
widthPixels: Int,
|
|
174
|
+
displayScale: CGFloat,
|
|
175
|
+
leaseHandle: UInt64,
|
|
176
|
+
allowCompletedFallback: Bool = false,
|
|
177
|
+
shouldAcquire: () -> Bool = { true }
|
|
178
|
+
) -> PreparedProseLayout? {
|
|
179
|
+
condition.lock()
|
|
180
|
+
defer { condition.unlock() }
|
|
181
|
+
|
|
182
|
+
let requestedMountKey = ProseMountKey(
|
|
183
|
+
generationIdentity: generationIdentity,
|
|
184
|
+
widthPixels: widthPixels,
|
|
185
|
+
displayScale: displayScale
|
|
186
|
+
)
|
|
187
|
+
// UIKit-only callers use the documented zero token because they have
|
|
188
|
+
// no Fabric owner. They retain the ordinary completed-cache lookup.
|
|
189
|
+
if surface.surfaceId == 0, surface.componentTag == 0,
|
|
190
|
+
let key = mountIndex[requestedMountKey], let layout = completed[key] {
|
|
191
|
+
touch(key)
|
|
192
|
+
return layout
|
|
193
|
+
}
|
|
194
|
+
let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
|
|
195
|
+
if let leaseKey = (pendingLeaseKeysByOwner[owner] ?? [])
|
|
196
|
+
.filter({
|
|
197
|
+
$0.layout.generationIdentity == requestedMountKey.generationIdentity
|
|
198
|
+
&& $0.layout.displayScaleBits == requestedMountKey.displayScaleBits
|
|
199
|
+
&& abs($0.layout.widthPixels - widthPixels) <= Self.pixelGridRoundingSlackPixels
|
|
200
|
+
})
|
|
201
|
+
.min(by: {
|
|
202
|
+
(abs($0.layout.widthPixels - widthPixels), $0.layout.widthPixels)
|
|
203
|
+
< (abs($1.layout.widthPixels - widthPixels), $1.layout.widthPixels)
|
|
204
|
+
}),
|
|
205
|
+
let layout = pendingLeases[leaseKey] {
|
|
206
|
+
guard shouldAcquire() else { return nil }
|
|
207
|
+
removePendingLeaseLocked(leaseKey)
|
|
208
|
+
for stale in mountedLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
|
|
209
|
+
removeMountedLeaseLocked(stale)
|
|
210
|
+
}
|
|
211
|
+
insertMountedLeaseLocked(layout, for: leaseKey)
|
|
212
|
+
enforceBudgetLocked()
|
|
213
|
+
return layout
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
guard allowCompletedFallback,
|
|
217
|
+
shouldAcquire(),
|
|
218
|
+
let key = mountIndex[requestedMountKey],
|
|
219
|
+
let layout = completed[key]
|
|
220
|
+
else { return nil }
|
|
221
|
+
let leaseKey = FabricLeaseKey(surface: surface, layout: key, leaseHandle: leaseHandle)
|
|
222
|
+
for stale in mountedLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
|
|
223
|
+
removeMountedLeaseLocked(stale)
|
|
224
|
+
}
|
|
225
|
+
insertMountedLeaseLocked(layout, for: leaseKey)
|
|
226
|
+
touch(key)
|
|
227
|
+
enforceBudgetLocked()
|
|
228
|
+
return layout
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
func releaseLease(
|
|
232
|
+
for surface: FabricSurfaceToken,
|
|
233
|
+
generationIdentity: String? = nil,
|
|
234
|
+
leaseHandle: UInt64? = nil
|
|
235
|
+
) {
|
|
236
|
+
condition.lock()
|
|
237
|
+
let pending = matchingLeaseKeysLocked(
|
|
238
|
+
in: pendingLeaseKeysBySurface[surface] ?? [],
|
|
239
|
+
generationIdentity: generationIdentity,
|
|
240
|
+
leaseHandle: leaseHandle
|
|
241
|
+
)
|
|
242
|
+
let mounted = matchingLeaseKeysLocked(
|
|
243
|
+
in: mountedLeaseKeysBySurface[surface] ?? [],
|
|
244
|
+
generationIdentity: generationIdentity,
|
|
245
|
+
leaseHandle: leaseHandle
|
|
246
|
+
)
|
|
247
|
+
pending.forEach(removePendingLeaseLocked)
|
|
248
|
+
mounted.forEach(removeMountedLeaseLocked)
|
|
249
|
+
// Released mounted artifacts may still have completed entries. Those
|
|
250
|
+
// entries re-enter the disposable budget on this transition.
|
|
251
|
+
enforceBudgetLocked()
|
|
252
|
+
condition.unlock()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// Commits a replacement generation for one state-family handle. Pending
|
|
256
|
+
/// G1 handoffs are stale immediately; mounted G1 remains visible until
|
|
257
|
+
/// G2's exact pending handoff is acquired by the normal mount path.
|
|
258
|
+
func activateFabricGeneration(
|
|
259
|
+
surface: FabricSurfaceToken,
|
|
260
|
+
generationIdentity: String,
|
|
261
|
+
leaseHandle: UInt64
|
|
262
|
+
) {
|
|
263
|
+
condition.lock()
|
|
264
|
+
let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
|
|
265
|
+
for stale in pendingLeaseKeysByOwner[owner] ?? [] where stale.layout.generationIdentity != generationIdentity {
|
|
266
|
+
removePendingLeaseLocked(stale)
|
|
267
|
+
}
|
|
268
|
+
retireUnownedPublicationKeysLocked()
|
|
269
|
+
publishOwnerBytesLocked()
|
|
270
|
+
condition.unlock()
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// Removes only the unmounted handoff requested by a stale Fabric mount
|
|
274
|
+
/// callback. Mounted ownership is deliberately preserved: it may be the
|
|
275
|
+
/// currently displayed width while another width is pending.
|
|
276
|
+
///
|
|
277
|
+
/// Returns whether another lease for the same surface/generation remains,
|
|
278
|
+
/// so the registry can retain its generation-scoped compiler pin.
|
|
279
|
+
func releasePendingLease(
|
|
280
|
+
for surface: FabricSurfaceToken,
|
|
281
|
+
generationIdentity: String,
|
|
282
|
+
widthPixels: Int,
|
|
283
|
+
displayScale: CGFloat,
|
|
284
|
+
leaseHandle: UInt64
|
|
285
|
+
) -> Bool {
|
|
286
|
+
condition.lock()
|
|
287
|
+
defer { condition.unlock() }
|
|
288
|
+
|
|
289
|
+
let requested = ProseMountKey(
|
|
290
|
+
generationIdentity: generationIdentity,
|
|
291
|
+
widthPixels: widthPixels,
|
|
292
|
+
displayScale: displayScale
|
|
293
|
+
)
|
|
294
|
+
let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
|
|
295
|
+
if let leaseKey = pendingLeaseKeysByOwner[owner]?.first,
|
|
296
|
+
mountKey(for: leaseKey.layout) == requested {
|
|
297
|
+
removePendingLeaseLocked(leaseKey)
|
|
298
|
+
retireUnownedPublicationKeysLocked()
|
|
299
|
+
publishOwnerBytesLocked()
|
|
300
|
+
}
|
|
301
|
+
return (pendingLeaseKeysByOwner[owner] ?? []).contains { $0.layout.generationIdentity == generationIdentity }
|
|
302
|
+
|| (mountedLeaseKeysByOwner[owner] ?? []).contains { $0.layout.generationIdentity == generationIdentity }
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/// Surface shutdown must release mounted artifacts even after memory
|
|
306
|
+
/// pressure has already cleared registry compiler ownership. Return the
|
|
307
|
+
/// exact lease identities before mutating this bounded cache.
|
|
308
|
+
func fabricGenerations(for surface: FabricSurfaceToken) -> Set<FabricGenerationToken> {
|
|
309
|
+
condition.lock()
|
|
310
|
+
defer { condition.unlock() }
|
|
311
|
+
let leaseKeys = (pendingLeaseKeysBySurface[surface] ?? [])
|
|
312
|
+
.union(mountedLeaseKeysBySurface[surface] ?? [])
|
|
313
|
+
return Set(leaseKeys.map {
|
|
314
|
+
FabricGenerationToken(
|
|
315
|
+
surface: $0.surface,
|
|
316
|
+
generationIdentity: $0.layout.generationIdentity,
|
|
317
|
+
leaseHandle: $0.leaseHandle
|
|
318
|
+
)
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
func registerDirectMount(_ owner: String, layout: PreparedProseLayout) {
|
|
323
|
+
condition.lock()
|
|
324
|
+
insertDirectMountLocked(layout, for: owner)
|
|
325
|
+
// Replacing an owner can return its prior completed entry to the
|
|
326
|
+
// disposable budget even as the new artifact becomes direct-mounted.
|
|
327
|
+
enforceBudgetLocked()
|
|
328
|
+
condition.unlock()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func releaseDirectMount(_ owner: String) {
|
|
332
|
+
condition.lock()
|
|
333
|
+
removeDirectMountLocked(owner)
|
|
334
|
+
// A direct mount is excluded from the disposable-cache budget while
|
|
335
|
+
// it owns the artifact. Once released, its completed entry becomes
|
|
336
|
+
// unmounted again and must be evicted before telemetry observes it.
|
|
337
|
+
enforceBudgetLocked()
|
|
338
|
+
condition.unlock()
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
func removeAllUnmounted() {
|
|
342
|
+
condition.lock()
|
|
343
|
+
while let key = completed.keys.first { removeCompletedLocked(key) }
|
|
344
|
+
accessOrder.removeAll()
|
|
345
|
+
accessGenerations.removeAll()
|
|
346
|
+
accessOrderHead = 0
|
|
347
|
+
mountIndex.removeAll()
|
|
348
|
+
while let key = pendingLeases.keys.first { removePendingLeaseLocked(key) }
|
|
349
|
+
// Pending handoffs are unmounted owners, so memory pressure retires
|
|
350
|
+
// them with the completed cache. Fabric/direct mounted owners survive
|
|
351
|
+
// until their explicit release paths run. Registry clears compiled
|
|
352
|
+
// documents only after this unmounted cache step.
|
|
353
|
+
retireUnownedPublicationKeysLocked()
|
|
354
|
+
publishOwnerBytesLocked()
|
|
355
|
+
condition.unlock()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
var countForTesting: Int {
|
|
359
|
+
condition.lock()
|
|
360
|
+
defer { condition.unlock() }
|
|
361
|
+
return completed.count
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/// Benchmark reverse passes may not rebind a one-item or already-visible
|
|
365
|
+
/// cell. Seed only those passes with the exact live census from the
|
|
366
|
+
/// completed forward pass, then retain the normal end-of-pass liveness
|
|
367
|
+
/// intersection below.
|
|
368
|
+
func beginBenchmarkCensus(seeding keys: [ProseLayoutKey] = []) {
|
|
369
|
+
condition.lock()
|
|
370
|
+
benchmarkCensusKeys = Set(keys)
|
|
371
|
+
condition.unlock()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
func endBenchmarkCensus() -> [ProseLayoutKey] {
|
|
375
|
+
condition.lock()
|
|
376
|
+
defer { condition.unlock() }
|
|
377
|
+
// A census records keys observed during a benchmark pass, but it is
|
|
378
|
+
// evidence of resident artifacts only. A lookup can fail, produce an
|
|
379
|
+
// oversized unowned artifact, or evict an earlier completed entry;
|
|
380
|
+
// none of those keys is resident when the pass ends. Pending and
|
|
381
|
+
// mounted Fabric leases plus direct UIKit mounts are genuine live
|
|
382
|
+
// owners even when the disposable completed LRU no longer has them.
|
|
383
|
+
let liveKeys = Set(completed.keys)
|
|
384
|
+
.union(pendingLeaseKeysByLayout.keys)
|
|
385
|
+
.union(mountedLeaseKeysByLayout.keys)
|
|
386
|
+
.union(directOwnersByLayout.keys)
|
|
387
|
+
let keys = benchmarkCensusKeys.map { Array($0.intersection(liveKeys)) } ?? []
|
|
388
|
+
benchmarkCensusKeys = nil
|
|
389
|
+
return keys
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
var retainedBytesForTesting: Int {
|
|
393
|
+
condition.lock()
|
|
394
|
+
defer { condition.unlock() }
|
|
395
|
+
return retainedBytes
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
var oversizedLeaseCountForTesting: Int {
|
|
399
|
+
condition.lock()
|
|
400
|
+
defer { condition.unlock() }
|
|
401
|
+
return oversizedLeaseCount
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
var pendingLeaseCountForTesting: Int {
|
|
405
|
+
condition.lock()
|
|
406
|
+
defer { condition.unlock() }
|
|
407
|
+
return pendingLeases.count
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
var mountedLeaseCountForTesting: Int {
|
|
411
|
+
condition.lock()
|
|
412
|
+
defer { condition.unlock() }
|
|
413
|
+
return mountedLeases.count
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
var leaseCountForTesting: Int {
|
|
417
|
+
condition.lock()
|
|
418
|
+
defer { condition.unlock() }
|
|
419
|
+
return pendingLeases.count + mountedLeases.count
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
var accessOrderTokenCountForTesting: Int {
|
|
423
|
+
condition.lock()
|
|
424
|
+
defer { condition.unlock() }
|
|
425
|
+
return accessOrder.count
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private func createPendingLeaseLocked(
|
|
429
|
+
_ layout: PreparedProseLayout,
|
|
430
|
+
for key: ProseLayoutKey,
|
|
431
|
+
surface: FabricSurfaceToken,
|
|
432
|
+
leaseHandle: UInt64
|
|
433
|
+
) {
|
|
434
|
+
let leaseKey = FabricLeaseKey(surface: surface, layout: key, leaseHandle: leaseHandle)
|
|
435
|
+
// A new measurement supersedes only unmounted handoffs for this
|
|
436
|
+
// Fabric surface and generation. Keep a mounted artifact alive until
|
|
437
|
+
// the replacement is actually acquired by a component view.
|
|
438
|
+
let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
|
|
439
|
+
for stale in pendingLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
|
|
440
|
+
removePendingLeaseLocked(stale)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Repeated Yoga measurements for an already mounted identity neither
|
|
444
|
+
// replace that mounted artifact nor manufacture another handoff.
|
|
445
|
+
guard mountedLeases[leaseKey] == nil else {
|
|
446
|
+
retireUnownedPublicationKeysLocked()
|
|
447
|
+
publishOwnerBytesLocked()
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
insertPendingLeaseLocked(layout, for: leaseKey)
|
|
451
|
+
enforceBudgetLocked()
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private func removePendingLeaseLocked(_ key: FabricLeaseKey) {
|
|
455
|
+
guard let layout = pendingLeases.removeValue(forKey: key) else { return }
|
|
456
|
+
removeLeaseKey(key, from: &pendingLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
|
|
457
|
+
removeLeaseKey(key, from: &pendingLeaseKeysBySurface, key: key.surface)
|
|
458
|
+
removeLeaseKey(key, from: &pendingLeaseKeysByLayout, key: key.layout)
|
|
459
|
+
if layout.retainedBytes > byteBudget { oversizedLeaseCount -= 1 }
|
|
460
|
+
updateOwnershipLocked(layout, pendingDelta: -1, publicationKey: key.layout)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private func touch(_ key: ProseLayoutKey) {
|
|
464
|
+
guard completed[key] != nil else { return }
|
|
465
|
+
precondition(nextAccessGeneration < UInt64.max, "Prepared prose LRU generation overflowed.")
|
|
466
|
+
nextAccessGeneration += 1
|
|
467
|
+
accessGenerations[key] = nextAccessGeneration
|
|
468
|
+
accessOrder.append((key, nextAccessGeneration))
|
|
469
|
+
compactAccessOrderIfNeededLocked()
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private func mountedLayoutLocked(
|
|
473
|
+
for key: ProseLayoutKey,
|
|
474
|
+
fabricSurface: FabricSurfaceToken?,
|
|
475
|
+
fabricLeaseHandle: UInt64?
|
|
476
|
+
) -> PreparedProseLayout? {
|
|
477
|
+
if let fabricSurface {
|
|
478
|
+
// Fabric handoffs are owner-isolated: an artifact mounted by one
|
|
479
|
+
// surface must never satisfy another surface's measurement.
|
|
480
|
+
guard let fabricLeaseHandle else { return nil }
|
|
481
|
+
return mountedLeases[FabricLeaseKey(
|
|
482
|
+
surface: fabricSurface,
|
|
483
|
+
layout: key,
|
|
484
|
+
leaseHandle: fabricLeaseHandle
|
|
485
|
+
)]
|
|
486
|
+
}
|
|
487
|
+
// UIKit has no Fabric lease owner. A direct mounted view is still an
|
|
488
|
+
// exact immutable artifact for this semantic/physical key and remains
|
|
489
|
+
// valid after unmounted cache eviction.
|
|
490
|
+
guard let owner = directOwnersByLayout[key]?.first else { return nil }
|
|
491
|
+
return directMounted[owner]
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/// Lookup after same-owner mounted and completed entries. Pending, mounted,
|
|
495
|
+
/// and direct owners all retain the same immutable artifact contract even
|
|
496
|
+
/// if the completed cache was evicted or cleared under memory pressure.
|
|
497
|
+
private func liveLayoutLocked(for key: ProseLayoutKey) -> PreparedProseLayout? {
|
|
498
|
+
if let leaseKey = pendingLeaseKeysByLayout[key]?.first, let layout = pendingLeases[leaseKey] {
|
|
499
|
+
return layout
|
|
500
|
+
}
|
|
501
|
+
if let leaseKey = mountedLeaseKeysByLayout[key]?.first, let layout = mountedLeases[leaseKey] {
|
|
502
|
+
return layout
|
|
503
|
+
}
|
|
504
|
+
if let owner = directOwnersByLayout[key]?.first {
|
|
505
|
+
return directMounted[owner]
|
|
506
|
+
}
|
|
507
|
+
return nil
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private func mountKey(for layoutKey: ProseLayoutKey) -> ProseMountKey {
|
|
511
|
+
ProseMountKey(generationIdentity: layoutKey.generationIdentity, widthPixels: layoutKey.widthPixels, displayScaleBits: layoutKey.displayScaleBits)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
private func removeCompletedLocked(_ key: ProseLayoutKey) {
|
|
515
|
+
guard let layout = completed.removeValue(forKey: key) else { return }
|
|
516
|
+
accessGenerations.removeValue(forKey: key)
|
|
517
|
+
let mountKey = mountKey(for: key)
|
|
518
|
+
if mountIndex[mountKey] == key { mountIndex.removeValue(forKey: mountKey) }
|
|
519
|
+
updateOwnershipLocked(layout, completedDelta: -1, publicationKey: key)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
private func enforceBudgetLocked() {
|
|
523
|
+
// Completed entries are a disposable LRU. A pending Yoga-to-Fabric
|
|
524
|
+
// handoff is an exact active owner: evicting it would make Fabric's
|
|
525
|
+
// later mount miss without any completed-cache fallback. Its lifecycle
|
|
526
|
+
// release, activation replacement, or explicit memory warning owns
|
|
527
|
+
// retirement instead of ordinary byte pressure.
|
|
528
|
+
while budgetedRetainedBytes > byteBudget {
|
|
529
|
+
if let oldest = oldestCompletedKeyLocked() {
|
|
530
|
+
removeCompletedLocked(oldest)
|
|
531
|
+
continue
|
|
532
|
+
}
|
|
533
|
+
break
|
|
534
|
+
}
|
|
535
|
+
retireUnownedPublicationKeysLocked()
|
|
536
|
+
publishOwnerBytesLocked()
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
private func retireUnownedPublicationKeysLocked() {
|
|
540
|
+
#if DEBUG
|
|
541
|
+
for key in publicationRetirementCandidates where livePublicationCounts[key, default: 0] == 0 {
|
|
542
|
+
publishedKeys.remove(key)
|
|
543
|
+
}
|
|
544
|
+
publicationRetirementCandidates.removeAll()
|
|
545
|
+
#endif
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private func publishOwnerBytesLocked() {
|
|
549
|
+
PreparedProseInstrumentation.cacheUpdated(
|
|
550
|
+
unmountedBytes: unmountedRetainedBytes,
|
|
551
|
+
unmountedResidentCount: completed.count
|
|
552
|
+
)
|
|
553
|
+
PreparedProseInstrumentation.retained(.unmountedLayout, scope: "cache", bytes: unmountedRetainedBytes)
|
|
554
|
+
PreparedProseInstrumentation.retained(.fabricLeaseHandoff, scope: "leases", bytes: fabricLeaseRetainedBytes)
|
|
555
|
+
PreparedProseInstrumentation.retained(.directMounted, scope: "views", bytes: directMountedRetainedBytes)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private func insertCompletedLocked(_ layout: PreparedProseLayout, for key: ProseLayoutKey) {
|
|
559
|
+
if completed[key] != nil {
|
|
560
|
+
removeCompletedLocked(key)
|
|
561
|
+
}
|
|
562
|
+
completed[key] = layout
|
|
563
|
+
mountIndex[mountKey(for: key)] = key
|
|
564
|
+
updateOwnershipLocked(layout, completedDelta: 1, publicationKey: key)
|
|
565
|
+
touch(key)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
private func insertPendingLeaseLocked(_ layout: PreparedProseLayout, for key: FabricLeaseKey) {
|
|
569
|
+
removePendingLeaseLocked(key)
|
|
570
|
+
pendingLeases[key] = layout
|
|
571
|
+
insertLeaseKey(key, into: &pendingLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
|
|
572
|
+
insertLeaseKey(key, into: &pendingLeaseKeysBySurface, key: key.surface)
|
|
573
|
+
insertLeaseKey(key, into: &pendingLeaseKeysByLayout, key: key.layout)
|
|
574
|
+
if layout.retainedBytes > byteBudget { oversizedLeaseCount += 1 }
|
|
575
|
+
updateOwnershipLocked(layout, pendingDelta: 1, publicationKey: key.layout)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
private func insertMountedLeaseLocked(_ layout: PreparedProseLayout, for key: FabricLeaseKey) {
|
|
579
|
+
removeMountedLeaseLocked(key)
|
|
580
|
+
mountedLeases[key] = layout
|
|
581
|
+
insertLeaseKey(key, into: &mountedLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
|
|
582
|
+
insertLeaseKey(key, into: &mountedLeaseKeysBySurface, key: key.surface)
|
|
583
|
+
insertLeaseKey(key, into: &mountedLeaseKeysByLayout, key: key.layout)
|
|
584
|
+
if layout.retainedBytes > byteBudget { oversizedLeaseCount += 1 }
|
|
585
|
+
updateOwnershipLocked(layout, mountedDelta: 1, publicationKey: key.layout)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private func removeMountedLeaseLocked(_ key: FabricLeaseKey) {
|
|
589
|
+
guard let layout = mountedLeases.removeValue(forKey: key) else { return }
|
|
590
|
+
removeLeaseKey(key, from: &mountedLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
|
|
591
|
+
removeLeaseKey(key, from: &mountedLeaseKeysBySurface, key: key.surface)
|
|
592
|
+
removeLeaseKey(key, from: &mountedLeaseKeysByLayout, key: key.layout)
|
|
593
|
+
if layout.retainedBytes > byteBudget { oversizedLeaseCount -= 1 }
|
|
594
|
+
updateOwnershipLocked(layout, mountedDelta: -1, publicationKey: key.layout)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private func insertDirectMountLocked(_ layout: PreparedProseLayout, for owner: String) {
|
|
598
|
+
removeDirectMountLocked(owner)
|
|
599
|
+
directMounted[owner] = layout
|
|
600
|
+
directOwnersByLayout[layout.key, default: []].insert(owner)
|
|
601
|
+
updateOwnershipLocked(layout, directDelta: 1, publicationKey: layout.key)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private func removeDirectMountLocked(_ owner: String) {
|
|
605
|
+
guard let layout = directMounted.removeValue(forKey: owner) else { return }
|
|
606
|
+
directOwnersByLayout[layout.key]?.remove(owner)
|
|
607
|
+
if directOwnersByLayout[layout.key]?.isEmpty == true { directOwnersByLayout.removeValue(forKey: layout.key) }
|
|
608
|
+
updateOwnershipLocked(layout, directDelta: -1, publicationKey: layout.key)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private func insertLeaseKey<Index: Hashable>(
|
|
612
|
+
_ key: FabricLeaseKey,
|
|
613
|
+
into table: inout [Index: Set<FabricLeaseKey>],
|
|
614
|
+
key indexKey: Index
|
|
615
|
+
) {
|
|
616
|
+
table[indexKey, default: []].insert(key)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private func removeLeaseKey<Index: Hashable>(
|
|
620
|
+
_ key: FabricLeaseKey,
|
|
621
|
+
from table: inout [Index: Set<FabricLeaseKey>],
|
|
622
|
+
key indexKey: Index
|
|
623
|
+
) {
|
|
624
|
+
table[indexKey]?.remove(key)
|
|
625
|
+
if table[indexKey]?.isEmpty == true { table.removeValue(forKey: indexKey) }
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
private func matchingLeaseKeysLocked(
|
|
629
|
+
in keys: Set<FabricLeaseKey>,
|
|
630
|
+
generationIdentity: String?,
|
|
631
|
+
leaseHandle: UInt64?
|
|
632
|
+
) -> [FabricLeaseKey] {
|
|
633
|
+
keys.filter {
|
|
634
|
+
(generationIdentity == nil || $0.layout.generationIdentity == generationIdentity) &&
|
|
635
|
+
(leaseHandle == nil || $0.leaseHandle == leaseHandle)
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private func oldestCompletedKeyLocked() -> ProseLayoutKey? {
|
|
640
|
+
var skippedPinnedEntry = false
|
|
641
|
+
while accessOrderHead < accessOrder.count {
|
|
642
|
+
let token = accessOrder[accessOrderHead]
|
|
643
|
+
accessOrderHead += 1
|
|
644
|
+
guard accessGenerations[token.key] == token.generation,
|
|
645
|
+
let layout = completed[token.key],
|
|
646
|
+
let ownership = ownershipByIdentifier[ObjectIdentifier(layout)]
|
|
647
|
+
else { continue }
|
|
648
|
+
|
|
649
|
+
if ownership.pendingReferences == 0,
|
|
650
|
+
ownership.mountedReferences == 0,
|
|
651
|
+
ownership.directReferences == 0 {
|
|
652
|
+
// A pinned entry ahead of this candidate still needs its LRU
|
|
653
|
+
// token when it later returns to the budget. Rebuild only on
|
|
654
|
+
// this cold eviction path to preserve that ordering.
|
|
655
|
+
if skippedPinnedEntry {
|
|
656
|
+
rebuildAccessOrderLocked(excluding: token)
|
|
657
|
+
} else {
|
|
658
|
+
compactAccessOrderIfNeededLocked()
|
|
659
|
+
}
|
|
660
|
+
return token.key
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
skippedPinnedEntry = true
|
|
664
|
+
}
|
|
665
|
+
// Every remaining completed entry is pinned. Keep their current LRU
|
|
666
|
+
// tokens so a later release can make one evictable without a new hit.
|
|
667
|
+
rebuildAccessOrderLocked()
|
|
668
|
+
return nil
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private func rebuildAccessOrderLocked(
|
|
672
|
+
excluding excluded: (key: ProseLayoutKey, generation: UInt64)? = nil
|
|
673
|
+
) {
|
|
674
|
+
accessOrder = accessOrder.filter { token in
|
|
675
|
+
guard accessGenerations[token.key] == token.generation, completed[token.key] != nil else {
|
|
676
|
+
return false
|
|
677
|
+
}
|
|
678
|
+
return excluded.map { $0.key != token.key || $0.generation != token.generation } ?? true
|
|
679
|
+
}
|
|
680
|
+
accessOrderHead = 0
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
private func compactAccessOrderIfNeededLocked() {
|
|
684
|
+
let liveTokenCount = accessOrder.count - accessOrderHead
|
|
685
|
+
let compactionThreshold = max(64, completed.count * 3)
|
|
686
|
+
guard liveTokenCount > compactionThreshold || accessOrderHead > compactionThreshold else { return }
|
|
687
|
+
accessOrder = accessOrder[accessOrderHead...].filter { token in
|
|
688
|
+
accessGenerations[token.key] == token.generation && completed[token.key] != nil
|
|
689
|
+
}
|
|
690
|
+
accessOrderHead = 0
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private func updateOwnershipLocked(
|
|
694
|
+
_ layout: PreparedProseLayout,
|
|
695
|
+
completedDelta: Int = 0,
|
|
696
|
+
pendingDelta: Int = 0,
|
|
697
|
+
mountedDelta: Int = 0,
|
|
698
|
+
directDelta: Int = 0,
|
|
699
|
+
publicationKey: ProseLayoutKey
|
|
700
|
+
) {
|
|
701
|
+
let identifier = ObjectIdentifier(layout)
|
|
702
|
+
var ownership = ownershipByIdentifier[identifier] ?? LayoutOwnership(layout: layout)
|
|
703
|
+
let previous = ownership
|
|
704
|
+
ownership.completedReferences += completedDelta
|
|
705
|
+
ownership.pendingReferences += pendingDelta
|
|
706
|
+
ownership.mountedReferences += mountedDelta
|
|
707
|
+
ownership.directReferences += directDelta
|
|
708
|
+
precondition(
|
|
709
|
+
ownership.completedReferences >= 0 && ownership.pendingReferences >= 0 &&
|
|
710
|
+
ownership.mountedReferences >= 0 && ownership.directReferences >= 0,
|
|
711
|
+
"Prepared prose ownership references must not underflow."
|
|
712
|
+
)
|
|
713
|
+
applyOwnershipContributionDelta(from: previous, to: ownership)
|
|
714
|
+
if ownership.completedReferences + ownership.pendingReferences + ownership.mountedReferences + ownership.directReferences == 0 {
|
|
715
|
+
ownershipByIdentifier.removeValue(forKey: identifier)
|
|
716
|
+
} else {
|
|
717
|
+
ownershipByIdentifier[identifier] = ownership
|
|
718
|
+
}
|
|
719
|
+
#if DEBUG
|
|
720
|
+
let previousLiveCount = livePublicationCounts[publicationKey, default: 0]
|
|
721
|
+
let nextLiveCount = previousLiveCount + completedDelta + pendingDelta + mountedDelta + directDelta
|
|
722
|
+
precondition(nextLiveCount >= 0, "Prepared prose publication references must not underflow.")
|
|
723
|
+
if nextLiveCount == 0 {
|
|
724
|
+
livePublicationCounts.removeValue(forKey: publicationKey)
|
|
725
|
+
publicationRetirementCandidates.insert(publicationKey)
|
|
726
|
+
} else {
|
|
727
|
+
livePublicationCounts[publicationKey] = nextLiveCount
|
|
728
|
+
publicationRetirementCandidates.remove(publicationKey)
|
|
729
|
+
}
|
|
730
|
+
#endif
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private func applyOwnershipContributionDelta(from previous: LayoutOwnership, to next: LayoutOwnership) {
|
|
734
|
+
let bytes = next.layout.retainedBytes
|
|
735
|
+
retainedBytes += contribution(next, bytes: bytes, kind: .retained) - contribution(previous, bytes: bytes, kind: .retained)
|
|
736
|
+
budgetedRetainedBytes += contribution(next, bytes: bytes, kind: .budgeted) - contribution(previous, bytes: bytes, kind: .budgeted)
|
|
737
|
+
unmountedRetainedBytes += contribution(next, bytes: bytes, kind: .unmounted) - contribution(previous, bytes: bytes, kind: .unmounted)
|
|
738
|
+
fabricLeaseRetainedBytes += contribution(next, bytes: bytes, kind: .fabricLease) - contribution(previous, bytes: bytes, kind: .fabricLease)
|
|
739
|
+
directMountedRetainedBytes += contribution(next, bytes: bytes, kind: .directMounted) - contribution(previous, bytes: bytes, kind: .directMounted)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
private enum OwnershipContribution {
|
|
743
|
+
case retained
|
|
744
|
+
case budgeted
|
|
745
|
+
case unmounted
|
|
746
|
+
case fabricLease
|
|
747
|
+
case directMounted
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
private func contribution(_ ownership: LayoutOwnership, bytes: Int, kind: OwnershipContribution) -> Int {
|
|
751
|
+
switch kind {
|
|
752
|
+
case .retained:
|
|
753
|
+
return ownership.completedReferences + ownership.pendingReferences + ownership.mountedReferences + ownership.directReferences > 0 ? bytes : 0
|
|
754
|
+
case .budgeted:
|
|
755
|
+
return ownership.completedReferences + ownership.pendingReferences > 0 &&
|
|
756
|
+
ownership.mountedReferences + ownership.directReferences == 0 ? bytes : 0
|
|
757
|
+
case .unmounted:
|
|
758
|
+
return ownership.completedReferences > 0 && ownership.pendingReferences == 0 &&
|
|
759
|
+
ownership.mountedReferences == 0 && ownership.directReferences == 0 ? bytes : 0
|
|
760
|
+
case .fabricLease:
|
|
761
|
+
return ownership.pendingReferences + ownership.mountedReferences > 0 && ownership.directReferences == 0 ? bytes : 0
|
|
762
|
+
case .directMounted:
|
|
763
|
+
return ownership.directReferences > 0 ? bytes : 0
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|