diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0436067aa..5274e49a0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -49,6 +49,8 @@ X.X.X
- [ Ex: Kosmos' distribution of Atmosphère ] - Do you have additional kips or sysmodules you're loading: - Homebrew software installed: [ * ] +- EmuMMC or SysNAND: + - [ If using an EmuMMC, include whether it's partition-based or file-based. ] ### Additional context? diff --git a/Makefile b/Makefile index 1784248cc..d2cc0ca4c 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,9 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro) +endif + +include $(DEVKITPRO)/devkitA64/base_tools + TOPTARGETS := all clean dist-no-debug dist AMSBRANCH := $(shell git symbolic-ref --short HEAD) AMSHASH := $(shell git rev-parse --short HEAD) @@ -37,6 +43,7 @@ libraries: clean: $(MAKE) -C fusee clean + $(MAKE) -C emummc clean rm -rf out dist-no-debug: all @@ -56,19 +63,11 @@ dist-no-debug: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000015 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042 mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates mkdir -p atmosphere-$(AMSVER)/atmosphere/config + mkdir -p atmosphere-$(AMSVER)/atmosphere/flags + touch atmosphere-$(AMSVER)/atmosphere/flags/clean_stratosphere_for_0.19.0.flag cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin cp fusee/fusee-secondary/fusee-secondary-experimental.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin @@ -85,20 +84,29 @@ dist-no-debug: all cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D/exefs.nsp - cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B/exefs.nsp - cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/exefs.nsp - cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034/exefs.nsp - cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp - cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/exefs.nsp - cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C/exefs.nsp - cp stratosphere/lm/lm.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000015/exefs.nsp - cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042/exefs.nsp - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags - touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags/boot2.flag - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags - touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000015 + cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp + cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B/exefs.nsp + cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032/exefs.nsp + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp + cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C/exefs.nsp + cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp + cp stratosphere/lm/lm.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000015/exefs.nsp + @build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs + rm -r atmosphere-$(AMSVER)/stratosphere_romfs cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; @@ -137,6 +145,7 @@ dist: dist-no-debug cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf + cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf diff --git a/config_templates/kip_patches/default_nogc/C86762BE19A51FA0C737AE921A816846605A64739523B4894F847BBED59E31EA.ips b/config_templates/kip_patches/default_nogc/C86762BE19A51FA0C737AE921A816846605A64739523B4894F847BBED59E31EA.ips new file mode 100644 index 000000000..4c945652d Binary files /dev/null and b/config_templates/kip_patches/default_nogc/C86762BE19A51FA0C737AE921A816846605A64739523B4894F847BBED59E31EA.ips differ diff --git a/config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips b/config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips new file mode 100644 index 000000000..0cc94013d Binary files /dev/null and b/config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips differ diff --git a/config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips b/config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips new file mode 100644 index 000000000..0cc94013d Binary files /dev/null and b/config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips differ diff --git a/config_templates/kip_patches/default_nogc/E1E8D3D6A2FE0B102CC79F8310EEEF66E5D14BCA2ED73454455FD87C615EEDC0.ips b/config_templates/kip_patches/default_nogc/E1E8D3D6A2FE0B102CC79F8310EEEF66E5D14BCA2ED73454455FD87C615EEDC0.ips new file mode 100644 index 000000000..4c945652d Binary files /dev/null and b/config_templates/kip_patches/default_nogc/E1E8D3D6A2FE0B102CC79F8310EEEF66E5D14BCA2ED73454455FD87C615EEDC0.ips differ diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index 3059cb839..f02491abb 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -1,9 +1,13 @@ -; Disable uploading error reports to Nintendo [eupld] +; Disable uploading error reports to Nintendo ; upload_enabled = u8!0x0 +[usb] +; Enable USB 3.0 superspeed for homebrew +; 0 = USB 3.0 support is system default (usually disabled), 1 = USB 3.0 support is enabled. +; usb30_force_enabled = u8!0x0 +[ro] ; Control whether RO should ease its validation of NROs. ; (note: this is normally not necessary, and ips patches can be used.) -[ro] ; ease_nro_restriction = u8!0x1 ; Atmosphere custom settings [atmosphere] @@ -32,12 +36,6 @@ ; NOTE: EXPERIMENTAL ; If you do not know what you are doing, do not touch this yet. ; fsmitm_redirect_saves_to_sd = u8!0x0 -; Controls whether to enable the deprecated hid mitm -; to fix compatibility with old homebrew. -; 0 = Do not enable, 1 = Enable. -; Please note this setting may be removed in a -; future release of Atmosphere. -; enable_deprecated_hid_mitm = u8!0x0 ; Controls whether am sees system settings "DebugModeFlag" as ; enabled or disabled. ; 0 = Disabled (not debug mode), 1 = Enabled (debug mode) @@ -52,6 +50,9 @@ ; Controls whether dns.mitm logs to the sd card for debugging ; 0 = Disabled, 1 = Enabled ; enable_dns_mitm_debug_log = u8!0x0 +; Controls whether htc is enabled +; 0 = Disabled, 1 = Enabled +; enable_htc = u8!0x0 [hbloader] ; Controls the size of the homebrew heap when running as applet. ; If set to zero, all available applet memory is used as heap. diff --git a/docs/changelog.md b/docs/changelog.md index 0fa705e14..0f504fd7a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,99 @@ # Changelog +## 0.19.5 ++ Support was added for 12.1.0. ++ LayeredFS support was added for OpenDataStorageWithProgramIndex commands. + + Certain games using newer (7.0.0+ APIs) which include multiple programs under a single title previously could not be modified. + + These are now supported as normal, and LayeredFS should have 100% compatibility again. ++ A number of minor issues were fixed, including: + + The Reboot to Payload NRO was updated to allow the OS to save state prior to rebooting (thanks @AuroraWright)! + + An issue was fixed that could cause dns.mitm to fail when games requested resolution of an empty string. + + An issue was fixed that caused a memory leak in the erpt system module. + + This would eventually cause a system crash after ~540 reports were generated without rebooting. ++ A number of minor improvements were made to improve mesosphere's accuracy. ++ General system stability improvements to enhance the user's experience. +## 0.19.4 ++ Support was added for 12.0.3. ++ A number of minor issues were fixed, including: + + An issue was fixed that could cause heap memory corruption when allocation was highly contended. + + An issue was fixed that could cause sleep to fail under certain conditions. + + An issue was fixed that could cause a scheduler slow path to be taken more often than necessary. ++ General system stability improvements to enhance the user's experience. +## 0.19.3 ++ Support was added for 12.0.2. ++ A number of minor issues were fixed, including: + + An issue was fixed in dns.mitm that caused a crash when games attempted to resolve the IP address of nullptr. + + An issue was fixed in erpt that would cause an abort when booting without having ever booted stock previously. + + An issue was fixed in (file-based) emummc that caused an error on system format/downloading certain games. ++ General system stability improvements to enhance the user's experience. +## 0.19.2 ++ Atmosphère's components were further updated to reflect latest official behaviors as of 12.0.0. + + Notably, `erpt` was updated to implement the new forced shutdown detection feature. + + When a forced-shutdown occurs, an erpt_report will be generated and saved to the SD card on the next boot. ++ Atmosphere-libs was updated to use GCC 11 (latest devkitA64/devkitARM releases). + + Initial inspections show mild-to-moderate optimizer improvements in several important places (kernel is 0x3000 smaller). + + General system stability improvements to enhance the user's experience. ++ A number of minor issues were fixed, including: + + A bug was fixed that caused a black screen when attempting to boot firmware versions 2.0.0-4.1.0. + + A bug was fixed that caused sm to abort when at the session limit, rather than returning error codes. + + A bug was fixed that allowed for resource exhaustion on 12.0.0, under certain circumstances. ++ Several issues were fixed, and usability and stability were improved. +## 0.19.1 ++ An issue was fixed that caused a fatal error when using official `migration` services to transfer data between consoles. ++ An issue was fixed in `ncm` that caused an error when the OS tried to enumerate installed SD card content. ++ Several issues were fixed, and usability and stability were improved. +## 0.19.0 ++ Support was added for 12.0.0. + + `mesosphère` was updated to reflect the latest official kernel behavior. + + `sm`, `boot2`, `pgl` were updated to reflect the latest official behaviors. + + **Please Note**: 12.0.0 added a new protocol for IPC ("tipc"), which has been freshly reimplemented in its entirety. + + It is possible there may be as of yet unfound issues; if there are, please send the appropriate crash reports to SciresM (SciresM#0524 on discord). + + Homebrew which uses atmosphere extensions (including the mitm API) will need to be re-compiled in order to function on 0.19.0. + + I apologize for this, but it's unavoidable for technical reasons. If you're affected by this and mad about it, please contact SciresM to complain. + + `erpt` was partially updated to reflect the latest official behaviors. + + New features were added to erpt to track the activity of running applets, and to detect when a forced shutdown occurs. + + These behaviors have been temporarily stubbed, as they are not necessary for 12.0.0 to run (and their outputs won't be saved anywhere). + + A future atmosphère update will implement these behaviors, in the interest of reflecting official logic as faithfully as we can. ++ Atmosphère no longer uses the /contents/ folder for its own programs. + + Atmosphère's system modules are now bundled together in the single file "stratosphere.romfs". + + For those working on developing for atmosphère, executables inside the /contents/ directory will be preferred to those in "stratosphere.romfs". + + **Please Note**: In order to facilitate this change (and the desired behavior), the first time you boot after extracting a release zip, atmosphère system modules inside /contents/ will be deleted. + + This will have no impact on user programs (it only removes programs with specific program ids). ++ Improvements were made to mesosphere, including: + + An extension InfoType was added for getting the current process handle, without having to spawn a thread and do IPC with oneself. + + An issue was fixed in SvcSetDebugThreadContext. + + An issue was fixed when doing IPC with user buffers. ++ Support was fixed for toggling the custom setting `usb!usb30_force_enabled` on 9.0.0+. + + This was broken by Nintendo's introducing a dependency that made USB a requirement to launch before custom settings are parsed. + + Since the fix, you can now toggle the setting (as you could prior to atmosphère 0.9.4), and it will work as expected. + + **Please Note**: Enabling USB 3.0 often severely impacts wireless communications. + + Because of this, the setting will default to off. If you experience issues with it enabled, consider disabling it. ++ A warning was added to daybreak when resetting the console to factory settings. ++ Substantial work was completed towards atmosphere's upcoming implementation of the host target connection protocol. + + Once completed, users will be able to interact with a Switch running atmosphère via a PC application ("Starlink") currently under development. + + Planned eventual features for connected consoles include a gdbstub, interacting with memory (for cheat development), streaming gameplay audio and video, and accessing the Switch's SD card filesystem. + + Switch homebrew will also have access to a (configurable and sandboxed) filesystem on the host PC, while connected. + + Towards this end, the following was accomplished: + + The "htc" system module was reimplemented completely. + + The system module which provides remote access to the SD card was reimplemented completely. + + This is currently the active focus of atmosphère's development. + + **Please Note**: Support is not yet completed, and users are disadvised from interacting with the related settings for the time being, unless they particularly know what they're doing. ++ A number of minor issues were fixed, including: + + A bug was fixed in `dmnt` that could cause a fatal when launching certain games with cheats active. + + An issue was fixed that could cause an abort in `sm` when using a large number of custom system modules. + + An issue was fixed that prevented launching gamecards on 1.0.0. + + Minor issues were fixed in the cheat virtual machine's behavior. ++ Several issues were fixed, and usability and stability were improved. +## 0.18.1 ++ A number of minor issues were fixed, including: + + The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr. + + This fixes youtube ad-blocking, and possibly other usecases. + + A bug was fixed that caused ams.mitm to incorrectly cache data storages. + + This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases). + + A bug was fixed in power state control module registration. + + This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences. + + A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs. + + This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved. ++ Several issues were fixed, and usability and stability were improved. ## 0.18.0 + A new mitm module was added (`dns.mitm`). + This provides a highly configurable mechanism for redirecting DNS resolution requests. @@ -30,7 +125,7 @@ + This also substantially improves power drain when the system is shut off; consoles powered off from Atmosphere should now drain battery at the same reduced rate as original firmware. + A number of minor changes were made, including: + A number of inconsistencies in the build system were fixed. - + Fow those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked. + + For those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked. + This substantially improves build times during development iteration. + `sm` was updated to more accurately reflect how official code manages request deferral. + `mesosphère` was updated to more accurately reflect official kernel management of the trace buffer. diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index 425e31b33..743f45426 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -25,12 +25,6 @@ set_mitm enables intercepting requests to the system settings service. It curren + `ns` system module and games (to allow for overriding game locales) + All firmware debug settings requests (to allow modification of system settings not directly exposed to the user) -## dns_mitm - -dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames. - -For documentation, see [here](../../features/dns_mitm.md). - ### Firmware Version set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`. It modifies the `display_version` field of the returned system version, causing the version to display @@ -39,3 +33,8 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o ### System Settings set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters. It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format. + +## dns_mitm +dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames. + +For documentation, see [here](../../features/dns_mitm.md). diff --git a/docs/features/cheats.md b/docs/features/cheats.md index 16514bb32..f67236bba 100644 --- a/docs/features/cheats.md +++ b/docs/features/cheats.md @@ -19,6 +19,8 @@ This behavior ensures that cheat codes are only loaded when the user would want In cases where `dmnt` has not activated the cheat manager, but the user wants to make it do so anyway, the cheat manager's service API provides a `ForceOpenCheatProcess` command that homebrew can use. This command will cause the cheat manager to try to force itself to attach to the process. +In cases where `dmnt` has activated the cheat manager, but the user wants to use an alternate debugger, the cheat manager's service API provides a `ForceCloseCheatProcess` command that homebrew can use. This command will cause the cheat manager to detach itself from the process. + By default, all cheat codes listed in the loaded .txt file will be toggled on. This is configurable by the user by editing the `atmosphere!dmnt_cheats_enabled_by_default` [system setting](configurations.md). Users may use homebrew programs to toggle cheats on and off at runtime via the cheat manager's service API. @@ -40,30 +42,30 @@ The following provides documentation of the instruction format for the virtual m Typically, instruction type is encoded in the upper nybble of the first instruction u32. -### Code Type 0: Store Static Value to Memory -Code type 0 allows writing a static value to a memory address. +### Code Type 0x0: Store Static Value to Memory +Code type 0x0 allows writing a static value to a memory address. #### Encoding `0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)` + T: Width of memory write (1, 2, 4, or 8 bytes). -+ M: Memory region to write to (0 = Main NSO, 1 = Heap). ++ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr). + R: Register to use as an offset from memory region base. + A: Immediate offset to use from memory region base. + V: Value to write. --- -### Code Type 1: Begin Conditional Block -Code type 1 performs a comparison of the contents of memory to a static value. +### Code Type 0x1: Begin Conditional Block +Code type 0x1 performs a comparison of the contents of memory to a static value. -If the condition is not met, all instructions until the appropriate conditional block terminator are skipped. +If the condition is not met, all instructions until the appropriate End or Else conditional block terminator are skipped. #### Encoding `1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)` + T: Width of memory write (1, 2, 4, or 8 bytes). -+ M: Memory region to write to (0 = Main NSO, 1 = Heap). ++ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr). + C: Condition to use, see below. + A: Immediate offset to use from memory region base. + V: Value to compare to. @@ -78,16 +80,20 @@ If the condition is not met, all instructions until the appropriate conditional --- -### Code Type 2: End Conditional Block -Code type 2 marks the end of a conditional block (started by Code Type 1 or Code Type 8). +### Code Type 0x2: End Conditional Block +Code type 0x2 marks the end of a conditional block (started by Code Type 0x1 or Code Type 0x8). + +When an Else is executed, all instructions until the appropriate End conditional block terminator are skipped. #### Encoding -`20000000` +`2X000000` + ++ X: End type (0 = End, 1 = Else). --- -### Code Type 3: Start/End Loop -Code type 3 allows for iterating in a loop a fixed number of times. +### Code Type 0x3: Start/End Loop +Code type 0x3 allows for iterating in a loop a fixed number of times. #### Start Loop Encoding `300R0000 VVVVVVVV` @@ -102,8 +108,8 @@ Code type 3 allows for iterating in a loop a fixed number of times. --- -### Code Type 4: Load Register with Static Value -Code type 4 allows setting a register to a constant value. +### Code Type 0x4: Load Register with Static Value +Code type 0x4 allows setting a register to a constant value. #### Encoding `400R0000 VVVVVVVV VVVVVVVV` @@ -113,29 +119,28 @@ Code type 4 allows setting a register to a constant value. --- -### Code Type 5: Load Register with Memory Value -Code type 5 allows loading a value from memory into a register, either using a fixed address or by dereferencing the destination register. +### Code Type 0x5: Load Register with Memory Value +Code type 0x5 allows loading a value from memory into a register, either using a fixed address or by dereferencing the destination register. #### Load From Fixed Address Encoding `5TMR00AA AAAAAAAA` + T: Width of memory read (1, 2, 4, or 8 bytes). -+ M: Memory region to write to (0 = Main NSO, 1 = Heap). ++ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr). + R: Register to load value into. + A: Immediate offset to use from memory region base. #### Load from Register Address Encoding -`5TMR10AA AAAAAAAA` +`5T0R10AA AAAAAAAA` + T: Width of memory read (1, 2, 4, or 8 bytes). -+ M: Memory region to write to (0 = Main NSO, 1 = Heap). -+ R: Register to load value into. ++ R: Register to load value into. (This register is also used as the base memory address). + A: Immediate offset to use from register R. --- -### Code Type 6: Store Static Value to Register Memory Address -Code type 6 allows writing a fixed value to a memory address specified by a register. +### Code Type 0x6: Store Static Value to Register Memory Address +Code type 0x6 allows writing a fixed value to a memory address specified by a register. #### Encoding `6T0RIor0 VVVVVVVV VVVVVVVV` @@ -149,10 +154,10 @@ Code type 6 allows writing a fixed value to a memory address specified by a regi --- -### Code Type 7: Legacy Arithmetic -Code type 7 allows performing arithmetic on registers. +### Code Type 0x7: Legacy Arithmetic +Code type 0x7 allows performing arithmetic on registers. -However, it has been deprecated by Code type 9, and is only kept for backwards compatibility. +However, it has been deprecated by Code type 0x9, and is only kept for backwards compatibility. #### Encoding `7T0RC000 VVVVVVVV` @@ -171,8 +176,8 @@ However, it has been deprecated by Code type 9, and is only kept for backwards c --- -### Code Type 8: Begin Keypress Conditional Block -Code type 8 enters or skips a conditional block based on whether a key combination is pressed. +### Code Type 0x8: Begin Keypress Conditional Block +Code type 0x8 enters or skips a conditional block based on whether a key combination is pressed. #### Encoding `8kkkkkkk` @@ -213,8 +218,8 @@ Note: This is the direct output of `hidKeysDown()`. --- -### Code Type 9: Perform Arithmetic -Code type 9 allows performing arithmetic on registers. +### Code Type 0x9: Perform Arithmetic +Code type 0x9 allows performing arithmetic on registers. #### Register Arithmetic Encoding `9TCRS0s0` @@ -248,8 +253,8 @@ Code type 9 allows performing arithmetic on registers. --- -### Code Type 10: Store Register to Memory Address -Code type 10 allows writing a register to memory. +### Code Type 0xA: Store Register to Memory Address +Code type 0xA allows writing a register to memory. #### Encoding `ATSRIOxa (aaaaaaaa)` @@ -272,13 +277,13 @@ Code type 10 allows writing a register to memory. --- -### Code Type 11: Reserved -Code Type 11 is currently reserved for future use. +### Code Type 0xB: Reserved +Code Type 0xB is currently reserved for future use. --- -### Code Type 12-15: Extended-Width Instruction -Code Types 12-15 signal to the VM to treat the upper two nybbles of the first dword as instruction type, instead of just the upper nybble. +### Code Type 0xC-0xF: Extended-Width Instruction +Code Types 0xC-0xF signal to the VM to treat the upper two nybbles of the first dword as instruction type, instead of just the upper nybble. This reserves an additional 64 opcodes for future use. diff --git a/emummc/.gitrepo b/emummc/.gitrepo index 73225cc1e..8046e3e0d 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emuMMC branch = develop - commit = 5eed18eb527bbaa63aee5323c26de5b0cca6d28e - parent = 021b29d2dbc8ed0469bc822393e58c9f0d174d57 + commit = cbc294c390ed73bb281bc1028a8899c053427112 + parent = 38f9a76ba028995ed3274da3a45b0254f09d1f59 method = rebase cmdver = 0.4.1 diff --git a/emummc/Makefile b/emummc/Makefile index f902447aa..fbfcc6063 100644 --- a/emummc/Makefile +++ b/emummc/Makefile @@ -32,7 +32,7 @@ CFLAGS += $(INCLUDE) -D__SWITCH__ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +LDFLAGS = -specs=$(EMUMMCDIR)/emummc.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) ifneq ($(BUILD),$(notdir $(CURDIR))) diff --git a/emummc/emummc.ld b/emummc/emummc.ld new file mode 100644 index 000000000..48bf24b94 --- /dev/null +++ b/emummc/emummc.ld @@ -0,0 +1,201 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +PHDRS +{ + code PT_LOAD FLAGS(5) /* Read | Execute */; + rodata PT_LOAD FLAGS(4) /* Read */; + data PT_LOAD FLAGS(6) /* Read | Write */; + dyn PT_DYNAMIC; +} + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = 0x0); + . = __start__; + __code_start = . ; + + .crt0 : + { + KEEP (*(.crt0)) + . = ALIGN(8); + } :code + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } :code + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } :code + + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(8); + } :code + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } :code + + /* =========== RODATA section =========== */ + . = ALIGN(0x1000); + __rodata_start = . ; + + .nx-module-name : { KEEP (*(.nx-module-name)) } :rodata + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } :rodata + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } :rodata + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata + + .dynamic : { *(.dynamic) } :rodata :dyn + .dynsym : { *(.dynsym) } :rodata + .dynstr : { *(.dynstr) } :rodata + .rela.dyn : { *(.rela.*) } :rodata + .interp : { *(.interp) } :rodata + .hash : { *(.hash) } :rodata + .gnu.hash : { *(.gnu.hash) } :rodata + .gnu.version : { *(.gnu.version) } :rodata + .gnu.version_d : { *(.gnu.version_d) } :rodata + .gnu.version_r : { *(.gnu.version_r) } :rodata + .note.gnu.build-id : { *(.note.gnu.build-id) } :rodata + + /* =========== DATA section =========== */ + . = ALIGN(0x1000); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data + + .tdata ALIGN(8) : + { + __tdata_lma = .; + *(.tdata .tdata.* .gnu.linkonce.td.*) + . = ALIGN(8); + __tdata_lma_end = .; + } :data + + .tbss ALIGN(8) : + { + *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) + . = ALIGN(8); + } :data + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } :data + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP( *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)) ) + KEEP( *(.init_array .ctors) ) + PROVIDE (__init_array_end = .); + } :data + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP( *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)) ) + KEEP( *(.fini_array .dtors) ) + PROVIDE (__fini_array_end = .); + } :data + + __got_start__ = .; + + .got : { *(.got) *(.igot) } :data + .got.plt : { *(.got.plt) *(.igot.plt) } :data + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } :data + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(8); + + /* Reserve space for the TLS segment of the main thread */ + __tls_start = .; + . += + SIZEOF(.tdata) + SIZEOF(.tbss); + __tls_end = .; + } : data + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + . = ALIGN(0x1000); + __argdata__ = ABSOLUTE(.) ; + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} diff --git a/emummc/emummc.specs b/emummc/emummc.specs new file mode 100644 index 000000000..828da3d28 --- /dev/null +++ b/emummc/emummc.specs @@ -0,0 +1,8 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /emummc.ld) -pie --no-dynamic-linker --spare-dynamic-tags=0 --gc-sections -z text -z nodynamic-undefined-weak --build-id=sha1 --nx-module-name + +*startfile: +crti%O%s crtbegin%O%s + diff --git a/emummc/source/FS/FS_offsets.c b/emummc/source/FS/FS_offsets.c index 86dc12d6c..6165926aa 100644 --- a/emummc/source/FS/FS_offsets.c +++ b/emummc/source/FS/FS_offsets.c @@ -51,6 +51,10 @@ #include "offsets/1020_exfat.h" #include "offsets/1100.h" #include "offsets/1100_exfat.h" +#include "offsets/1200.h" +#include "offsets/1200_exfat.h" +#include "offsets/1203.h" +#include "offsets/1203_exfat.h" #include "../utils/fatal.h" #define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers @@ -113,6 +117,10 @@ DEFINE_OFFSET_STRUCT(_1020); DEFINE_OFFSET_STRUCT(_1020_EXFAT); DEFINE_OFFSET_STRUCT(_1100); DEFINE_OFFSET_STRUCT(_1100_EXFAT); +DEFINE_OFFSET_STRUCT(_1200); +DEFINE_OFFSET_STRUCT(_1200_EXFAT); +DEFINE_OFFSET_STRUCT(_1203); +DEFINE_OFFSET_STRUCT(_1203_EXFAT); const fs_offsets_t *get_fs_offsets(enum FS_VER version) { switch (version) { @@ -186,6 +194,14 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) { return &(GET_OFFSET_STRUCT_NAME(_1100)); case FS_VER_11_0_0_EXFAT: return &(GET_OFFSET_STRUCT_NAME(_1100_EXFAT)); + case FS_VER_12_0_0: + return &(GET_OFFSET_STRUCT_NAME(_1200)); + case FS_VER_12_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_1200_EXFAT)); + case FS_VER_12_0_3: + return &(GET_OFFSET_STRUCT_NAME(_1203)); + case FS_VER_12_0_3_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_1203_EXFAT)); default: fatal_abort(Fatal_UnknownVersion); } diff --git a/emummc/source/FS/FS_versions.h b/emummc/source/FS/FS_versions.h index 635210dcf..825d1a636 100644 --- a/emummc/source/FS/FS_versions.h +++ b/emummc/source/FS/FS_versions.h @@ -74,6 +74,12 @@ enum FS_VER FS_VER_11_0_0, FS_VER_11_0_0_EXFAT, + FS_VER_12_0_0, + FS_VER_12_0_0_EXFAT, + + FS_VER_12_0_3, + FS_VER_12_0_3_EXFAT, + FS_VER_MAX, }; diff --git a/emummc/source/FS/offsets/1200.h b/emummc/source/FS/offsets/1200.h new file mode 100644 index 000000000..c18d4340d --- /dev/null +++ b/emummc/source/FS/offsets/1200.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1200_H__ +#define __FS_1200_H__ + +// Accessor vtable getters +#define FS_OFFSET_1200_SDMMC_ACCESSOR_GC 0x154FD0 +#define FS_OFFSET_1200_SDMMC_ACCESSOR_SD 0x156DE0 +#define FS_OFFSET_1200_SDMMC_ACCESSOR_NAND 0x155500 + +// Hooks +#define FS_OFFSET_1200_SDMMC_WRAPPER_READ 0x150970 +#define FS_OFFSET_1200_SDMMC_WRAPPER_WRITE 0x150A30 +#define FS_OFFSET_1200_RTLD 0x688 +#define FS_OFFSET_1200_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1200_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0 + +// Misc funcs +#define FS_OFFSET_1200_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1200_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850 +#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0 + +// Misc Data +#define FS_OFFSET_1200_SD_MUTEX 0xE3D3E8 +#define FS_OFFSET_1200_NAND_MUTEX 0xE38768 +#define FS_OFFSET_1200_ACTIVE_PARTITION 0xE387A8 +#define FS_OFFSET_1200_SDMMC_DAS_HANDLE 0xE20DB0 + +// NOPs +#define FS_OFFSET_1200_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1200_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1200_H__ diff --git a/emummc/source/FS/offsets/1200_exfat.h b/emummc/source/FS/offsets/1200_exfat.h new file mode 100644 index 000000000..b6fb7ef93 --- /dev/null +++ b/emummc/source/FS/offsets/1200_exfat.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1200_EXFAT_H__ +#define __FS_1200_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_GC 0x154FD0 +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_SD 0x156DE0 +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_NAND 0x155500 + +// Hooks +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_READ 0x150970 +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_WRITE 0x150A30 +#define FS_OFFSET_1200_EXFAT_RTLD 0x688 +#define FS_OFFSET_1200_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1200_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0 + +// Misc funcs +#define FS_OFFSET_1200_EXFAT_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1200_EXFAT_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850 +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0 + +// Misc Data +#define FS_OFFSET_1200_EXFAT_SD_MUTEX 0xE4B3E8 +#define FS_OFFSET_1200_EXFAT_NAND_MUTEX 0xE46768 +#define FS_OFFSET_1200_EXFAT_ACTIVE_PARTITION 0xE467A8 +#define FS_OFFSET_1200_EXFAT_SDMMC_DAS_HANDLE 0xE2EDB0 + +// NOPs +#define FS_OFFSET_1200_EXFAT_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1200_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1200_EXFAT_H__ diff --git a/emummc/source/FS/offsets/1203.h b/emummc/source/FS/offsets/1203.h new file mode 100644 index 000000000..13aafa642 --- /dev/null +++ b/emummc/source/FS/offsets/1203.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * Copyright (c) 2021 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1203_H__ +#define __FS_1203_H__ + +// Accessor vtable getters +#define FS_OFFSET_1203_SDMMC_ACCESSOR_GC 0x1550E0 +#define FS_OFFSET_1203_SDMMC_ACCESSOR_SD 0x156EF0 +#define FS_OFFSET_1203_SDMMC_ACCESSOR_NAND 0x155610 + +// Hooks +#define FS_OFFSET_1203_SDMMC_WRAPPER_READ 0x150A80 +#define FS_OFFSET_1203_SDMMC_WRAPPER_WRITE 0x150B40 +#define FS_OFFSET_1203_RTLD 0x688 +#define FS_OFFSET_1203_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1203_CLKRST_SET_MIN_V_CLK_RATE 0x14FDD0 + +// Misc funcs +#define FS_OFFSET_1203_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1203_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1203_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150960 +#define FS_OFFSET_1203_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1509F0 + +// Misc Data +#define FS_OFFSET_1203_SD_MUTEX 0xE3D3E8 +#define FS_OFFSET_1203_NAND_MUTEX 0xE38768 +#define FS_OFFSET_1203_ACTIVE_PARTITION 0xE387A8 +#define FS_OFFSET_1203_SDMMC_DAS_HANDLE 0xE20DB0 + +// NOPs +#define FS_OFFSET_1203_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1203_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E920, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AFD0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081364, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092960, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1203_H__ diff --git a/emummc/source/FS/offsets/1203_exfat.h b/emummc/source/FS/offsets/1203_exfat.h new file mode 100644 index 000000000..016a1c4e0 --- /dev/null +++ b/emummc/source/FS/offsets/1203_exfat.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * Copyright (c) 2021 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1203_EXFAT_H__ +#define __FS_1203_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_GC 0x1550E0 +#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_SD 0x156EF0 +#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_NAND 0x155610 + +// Hooks +#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_READ 0x150A80 +#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_WRITE 0x150B40 +#define FS_OFFSET_1203_EXFAT_RTLD 0x688 +#define FS_OFFSET_1203_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1203_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x14FDD0 + +// Misc funcs +#define FS_OFFSET_1203_EXFAT_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1203_EXFAT_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150960 +#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1509F0 + +// Misc Data +#define FS_OFFSET_1203_EXFAT_SD_MUTEX 0xE4B3E8 +#define FS_OFFSET_1203_EXFAT_NAND_MUTEX 0xE46768 +#define FS_OFFSET_1203_EXFAT_ACTIVE_PARTITION 0xE467A8 +#define FS_OFFSET_1203_EXFAT_SDMMC_DAS_HANDLE 0xE2EDB0 + +// NOPs +#define FS_OFFSET_1203_EXFAT_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1203_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E920, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AFD0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081364, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092960, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1203_EXFAT_H__ diff --git a/emummc/source/emuMMC/emummc.c b/emummc/source/emuMMC/emummc.c index 49f1ca540..782b4837d 100644 --- a/emummc/source/emuMMC/emummc.c +++ b/emummc/source/emuMMC/emummc.c @@ -292,6 +292,36 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned { fp = &f_emu.fp_gpp[sector / f_emu.part_size]; sector = sector % f_emu.part_size; + + // Special handling for reads/writes which cross file-boundaries. + if (__builtin_expect(sector + num_sectors > f_emu.part_size, 0)) + { + unsigned int remaining = num_sectors; + while (remaining > 0) { + const unsigned int cur_sectors = MIN(remaining, f_emu.part_size - sector); + + if (f_lseek(fp, (u64)sector << 9) != FR_OK) + return 0; // Out of bounds. + + if (is_write) + { + if (f_write_fast(fp, buf, (u64)cur_sectors << 9) != FR_OK) + return 0; + } + else + { + if (f_read_fast(fp, buf, (u64)cur_sectors << 9) != FR_OK) + return 0; + } + + buf = (char *)buf + ((u64)cur_sectors << 9); + remaining -= cur_sectors; + sector = 0; + ++fp; + } + + return 1; + } } else { @@ -306,14 +336,14 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned break; } - if (f_lseek(fp, sector << 9) != FR_OK) + if (f_lseek(fp, (u64)sector << 9) != FR_OK) return 0; // Out of bounds. uint64_t res = 0; if (!is_write) - res = !f_read_fast(fp, buf, num_sectors << 9); + res = !f_read_fast(fp, buf, (u64)num_sectors << 9); else - res = !f_write_fast(fp, buf, num_sectors << 9); + res = !f_write_fast(fp, buf, (u64)num_sectors << 9); return res; } @@ -328,13 +358,13 @@ uint64_t sdmmc_wrapper_controller_open(int mmc_id) if (_this != NULL) { // Lock eMMC xfer while SD card is being initialized by FS. - if (_this == sdmmc_accessor_get(FS_SDMMC_SD)) + if (mmc_id == FS_SDMMC_SD) mutex_lock_handler(FS_SDMMC_EMMC); // Recursive Mutex, handler will lock SD as well if custom_driver result = _this->vtab->sdmmc_accessor_controller_open(_this); // Unlock eMMC. - if (_this == sdmmc_accessor_get(FS_SDMMC_SD)) + if (mmc_id == FS_SDMMC_SD) mutex_unlock_handler(FS_SDMMC_EMMC); return result; diff --git a/exosphere/loader_stub/loader_stub.specs b/exosphere/loader_stub/loader_stub.specs index 03984762f..261cffccd 100644 --- a/exosphere/loader_stub/loader_stub.specs +++ b/exosphere/loader_stub/loader_stub.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/mariko_fatal/mariko_fatal.specs b/exosphere/mariko_fatal/mariko_fatal.specs index e9b2e9fdf..71d294c07 100644 --- a/exosphere/mariko_fatal/mariko_fatal.specs +++ b/exosphere/mariko_fatal/mariko_fatal.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /mariko_fatal.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /mariko_fatal.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/program/program.specs b/exosphere/program/program.specs index 22b789960..7d174b721 100644 --- a/exosphere/program/program.specs +++ b/exosphere/program/program.specs @@ -1,4 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles \ No newline at end of file +%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/program/rebootstub/rebootstub.specs b/exosphere/program/rebootstub/rebootstub.specs index 4e41b1615..59b3762e3 100644 --- a/exosphere/program/rebootstub/rebootstub.specs +++ b/exosphere/program/rebootstub/rebootstub.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/program/sc7fw/sc7fw.specs b/exosphere/program/sc7fw/sc7fw.specs index 13455dc90..6275fb4d3 100644 --- a/exosphere/program/sc7fw/sc7fw.specs +++ b/exosphere/program/sc7fw/sc7fw.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/program/source/boot/secmon_boot_key_data.s b/exosphere/program/source/boot/secmon_boot_key_data.s index 7d54d4424..5204d5e23 100644 --- a/exosphere/program/source/boot/secmon_boot_key_data.s +++ b/exosphere/program/source/boot/secmon_boot_key_data.s @@ -85,10 +85,10 @@ _ZN3ams6secmon4boot15VolatileKeyDataE: /* We can get away with only including latest because exosphere supports newer-than-expected master key in engine. */ /* TODO: Update on next change of keys. */ /* Mariko Development Master Kek Source. */ -.byte 0xF9, 0x37, 0xCF, 0x9A, 0xBD, 0x86, 0xBB, 0xA9, 0x9C, 0x9E, 0x03, 0xC4, 0xFC, 0xBC, 0x3B, 0xCE +.byte 0x75, 0x2D, 0x2E, 0xF3, 0x2F, 0x3F, 0xFE, 0x65, 0xF4, 0xA9, 0x83, 0xB4, 0xED, 0x42, 0x63, 0xBA /* Mariko Production Master Kek Source. */ -.byte 0x0E, 0x44, 0x0C, 0xED, 0xB4, 0x36, 0xC0, 0x3F, 0xAA, 0x1D, 0xAE, 0xBF, 0x62, 0xB1, 0x09, 0x82 +.byte 0xE5, 0x41, 0xAC, 0xEC, 0xD1, 0xA7, 0xD1, 0xAB, 0xED, 0x03, 0x77, 0xF1, 0x27, 0xCA, 0xF8, 0xF1 /* Development Master Key Vectors. */ .byte 0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE /* Zeroes encrypted with Master Key 00. */ @@ -102,6 +102,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE: .byte 0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04 /* Master key 07 encrypted with Master key 08. */ .byte 0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE /* Master key 08 encrypted with Master key 09. */ .byte 0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4 /* Master key 09 encrypted with Master key 0A. */ +.byte 0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C /* Master key 0A encrypted with Master key 0B. */ /* Production Master Key Vectors. */ .byte 0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D /* Zeroes encrypted with Master Key 00. */ @@ -115,33 +116,37 @@ _ZN3ams6secmon4boot15VolatileKeyDataE: .byte 0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29 /* Master key 07 encrypted with Master key 08. */ .byte 0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80 /* Master key 08 encrypted with Master key 09. */ .byte 0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A /* Master key 09 encrypted with Master key 0A. */ +.byte 0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 /* Master key 0A encrypted with Master key 0B. */ /* Device Master Key Source Sources. */ -.byte 0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D /* 4.0.0 Device Master Key Source Source. */ -.byte 0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C /* 5.0.0 Device Master Key Source Source. */ -.byte 0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4 /* 6.0.0 Device Master Key Source Source. */ -.byte 0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17 /* 6.2.0 Device Master Key Source Source. */ -.byte 0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D /* 7.0.0 Device Master Key Source Source. */ -.byte 0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE /* 8.1.0 Device Master Key Source Source. */ -.byte 0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49 /* 9.0.0 Device Master Key Source Source. */ -.byte 0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94 /* 9.1.0 Device Master Key Source Source. */ +.byte 0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D /* 4.0.0 Device Master Key Source Source. */ +.byte 0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C /* 5.0.0 Device Master Key Source Source. */ +.byte 0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4 /* 6.0.0 Device Master Key Source Source. */ +.byte 0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17 /* 6.2.0 Device Master Key Source Source. */ +.byte 0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D /* 7.0.0 Device Master Key Source Source. */ +.byte 0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE /* 8.1.0 Device Master Key Source Source. */ +.byte 0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49 /* 9.0.0 Device Master Key Source Source. */ +.byte 0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94 /* 9.1.0 Device Master Key Source Source. */ +.byte 0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6 /* 12.1.0 Device Master Key Source Source. */ /* Development Device Master Kek Sources. */ -.byte 0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34 /* 4.0.0 Device Master Kek Source. */ -.byte 0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8 /* 5.0.0 Device Master Kek Source. */ -.byte 0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5 /* 6.0.0 Device Master Kek Source. */ -.byte 0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38 /* 6.2.0 Device Master Kek Source. */ -.byte 0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE /* 7.0.0 Device Master Kek Source. */ -.byte 0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87 /* 8.1.0 Device Master Kek Source. */ -.byte 0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F /* 9.0.0 Device Master Kek Source. */ -.byte 0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B /* 9.1.0 Device Master Kek Source. */ +.byte 0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34 /* 4.0.0 Device Master Kek Source. */ +.byte 0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8 /* 5.0.0 Device Master Kek Source. */ +.byte 0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5 /* 6.0.0 Device Master Kek Source. */ +.byte 0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38 /* 6.2.0 Device Master Kek Source. */ +.byte 0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE /* 7.0.0 Device Master Kek Source. */ +.byte 0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87 /* 8.1.0 Device Master Kek Source. */ +.byte 0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F /* 9.0.0 Device Master Kek Source. */ +.byte 0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B /* 9.1.0 Device Master Kek Source. */ +.byte 0xC4, 0xBB, 0xF3, 0x9F, 0xA3, 0xAA, 0x00, 0x99, 0x7C, 0x97, 0xAD, 0x91, 0x8F, 0xE8, 0x45, 0xCB /* 12.1.0 Device Master Kek Source. */ /* Production Device Master Kek Sources. */ -.byte 0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D /* 4.0.0 Device Master Kek Source. */ -.byte 0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E /* 5.0.0 Device Master Kek Source. */ -.byte 0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF /* 6.0.0 Device Master Kek Source. */ -.byte 0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB /* 6.2.0 Device Master Kek Source. */ -.byte 0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E /* 7.0.0 Device Master Kek Source. */ -.byte 0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D /* 8.1.0 Device Master Kek Source. */ -.byte 0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED /* 9.0.0 Device Master Kek Source. */ -.byte 0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36 /* 9.1.0 Device Master Kek Source. */ +.byte 0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D /* 4.0.0 Device Master Kek Source. */ +.byte 0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E /* 5.0.0 Device Master Kek Source. */ +.byte 0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF /* 6.0.0 Device Master Kek Source. */ +.byte 0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB /* 6.2.0 Device Master Kek Source. */ +.byte 0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E /* 7.0.0 Device Master Kek Source. */ +.byte 0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D /* 8.1.0 Device Master Kek Source. */ +.byte 0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED /* 9.0.0 Device Master Kek Source. */ +.byte 0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36 /* 9.1.0 Device Master Kek Source. */ +.byte 0xC2, 0x65, 0x34, 0x6E, 0xC7, 0xC6, 0x5D, 0x97, 0x3E, 0x34, 0x5C, 0x6B, 0xB3, 0x7E, 0xC6, 0xE3 /* 12.1.0 Device Master Kek Source. */ diff --git a/exosphere/program/source/boot/secmon_package2.cpp b/exosphere/program/source/boot/secmon_package2.cpp index 648d615fd..3e64a0304 100644 --- a/exosphere/program/source/boot/secmon_package2.cpp +++ b/exosphere/program/source/boot/secmon_package2.cpp @@ -94,7 +94,7 @@ namespace ams::secmon::boot { } /* Check that the key generation is one that we can use. */ - static_assert(pkg1::KeyGeneration_Count == 11); + static_assert(pkg1::KeyGeneration_Count == 12); if (key_generation >= pkg1::KeyGeneration_Count) { return false; } diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp index 158f249ae..86c347f8e 100644 --- a/exosphere/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere/program/source/smc/secmon_smc_aes.cpp @@ -272,7 +272,19 @@ namespace ams::secmon::smc { void GetSecureDataImpl(u8 *dst, SecureData which, bool tweak) { /* Compute the appropriate AES-CTR. */ - se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize); + { + /* Ensure that the SE sees consistent data. */ + hw::FlushDataCache(dst, AesKeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Perform the appropriate AES operation. */ + se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Ensure the CPU sees consistent data. */ + hw::FlushDataCache(dst, AesKeySize); + hw::DataSynchronizationBarrierInnerShareable(); + } /* Tweak, if we should. */ if (tweak) { diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 468b134fb..6b7fa0864 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -47,9 +47,9 @@ namespace ams::secmon::smc { [fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_AulaHynix1y4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaHynix1y4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB, - [fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagHynix1y4GB] = pkg1::MemorySize_4GB, [fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB, @@ -243,7 +243,7 @@ namespace ams::secmon::smc { (static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) | (static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) | (static_cast(GetKeyGeneration()) << 32) | - (static_cast(GetTargetFirmware()) << 00); + (static_cast(GetTargetFirmware()) << 0); break; case ConfigItem::ExosphereNeedsReboot: /* We are executing, so we aren't in the process of rebooting. */ @@ -286,6 +286,16 @@ namespace ams::secmon::smc { /* Get the log configuration. */ args.r[1] = (static_cast(static_cast(secmon::GetLogPort())) << 32) | static_cast(secmon::GetLogBaudRate()); break; + case ConfigItem::ExosphereForceEnableUsb30: + /* Get whether usb 3.0 should be force-enabled. */ + args.r[1] = GetSecmonConfiguration().IsUsb30ForceEnabled(); + break; + case ConfigItem::ExosphereSupportedHosVersion: + /* Get information about the supported hos version. */ + args.r[1] = (static_cast(ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR & 0xFF) << 24) | + (static_cast(ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR & 0xFF) << 16) | + (static_cast(ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO & 0xFF) << 8); + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp index 12870324e..fbe7917ae 100644 --- a/exosphere/program/source/smc/secmon_smc_info.hpp +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -40,16 +40,18 @@ namespace ams::secmon::smc { Package2Hash = 17, /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - ExosphereEmummcType = 65007, - ExospherePayloadAddress = 65008, - ExosphereLogConfiguration = 65009, + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, + ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, + ExosphereSupportedHosVersion = 65011, }; SmcResult SmcGetConfigUser(SmcArguments &args); diff --git a/exosphere/sdmmc_test/sdmmc_test.specs b/exosphere/sdmmc_test/sdmmc_test.specs index 72d846e06..fdf28d6e6 100644 --- a/exosphere/sdmmc_test/sdmmc_test.specs +++ b/exosphere/sdmmc_test/sdmmc_test.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp index e006ce8b7..aa173fcd6 100644 --- a/exosphere/warmboot/source/warmboot_main.cpp +++ b/exosphere/warmboot/source/warmboot_main.cpp @@ -36,7 +36,6 @@ namespace ams::warmboot { void Main(const Metadata *metadata) { /* Ensure that we're running under vaguely sane conditions. */ AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic); - AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max); /* Restrict the bpmp's access to dram. */ if (metadata->target_firmware >= TargetFirmware_4_0_0) { diff --git a/exosphere/warmboot/warmboot.specs b/exosphere/warmboot/warmboot.specs index 45d00560d..67747462e 100644 --- a/exosphere/warmboot/warmboot.specs +++ b/exosphere/warmboot/warmboot.specs @@ -1,7 +1,4 @@ %rename link old_link *link: -%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s +%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic \ No newline at end of file diff --git a/fusee/fusee-mtc/Makefile b/fusee/fusee-mtc/Makefile index 399733b10..d7977ff45 100644 --- a/fusee/fusee-mtc/Makefile +++ b/fusee/fusee-mtc/Makefile @@ -39,6 +39,7 @@ DEFINES := -D__BPMP__ -DFUSEE_MTC_SRC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" - CFLAGS := \ -g \ + -gdwarf-4 \ -O2 \ -fomit-frame-pointer \ -ffunction-sections \ @@ -46,6 +47,9 @@ CFLAGS := \ -std=gnu11 \ -Werror \ -Wall \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread \ -fstrict-volatile-bitfields \ $(ARCH) $(DEFINES) @@ -53,8 +57,8 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g -gdwarf-4 $(ARCH) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -g -gdwarf-4 $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index 7f99aae0b..8e203df39 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -46,6 +46,7 @@ DEFINES := -D__BPMP__ -DFUSEE_STAGE1_SRC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\ CFLAGS := \ -g \ + -gdwarf-4 \ -O2 \ -fomit-frame-pointer \ -ffunction-sections \ @@ -53,6 +54,9 @@ CFLAGS := \ -std=gnu11 \ -Werror \ -Wall \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread \ -fstrict-volatile-bitfields \ $(ARCH) $(DEFINES) @@ -60,8 +64,8 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g -gdwarf-4 $(ARCH) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -g -gdwarf-4 $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := diff --git a/fusee/fusee-primary/fusee-primary-main/Makefile b/fusee/fusee-primary/fusee-primary-main/Makefile index c6232b259..7ca99474b 100644 --- a/fusee/fusee-primary/fusee-primary-main/Makefile +++ b/fusee/fusee-primary/fusee-primary-main/Makefile @@ -39,6 +39,7 @@ DEFINES := -D__BPMP__ -DFUSEE_STAGE1_SRC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\ CFLAGS := \ -g \ + -gdwarf-4 \ -O2 \ -fomit-frame-pointer \ -ffunction-sections \ @@ -46,6 +47,9 @@ CFLAGS := \ -std=gnu11 \ -Werror \ -Wall \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread \ -fstrict-volatile-bitfields \ $(ARCH) $(DEFINES) @@ -53,8 +57,8 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g -gdwarf-4 $(ARCH) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -g -gdwarf-4 $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index e75679e76..818644d4a 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -48,6 +48,7 @@ DEFINES := -D__BPMP__ -DFUSEE_STAGE2_SRC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\ CFLAGS := \ -g \ + -gdwarf-4 \ -O2 \ -fomit-frame-pointer \ -ffunction-sections \ @@ -55,6 +56,9 @@ CFLAGS := \ -std=gnu11 \ -Werror \ -Wall \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread \ -fstrict-volatile-bitfields \ $(ARCH) $(DEFINES) @@ -62,8 +66,8 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -ASFLAGS := -g $(ARCH) $(INCLUDE) $(DEFINES) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g -gdwarf-4 $(ARCH) $(INCLUDE) $(DEFINES) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -g -gdwarf-4 $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := diff --git a/fusee/fusee-secondary/src/emummc_cfg.h b/fusee/fusee-secondary/src/emummc_cfg.h index 0099bd2d8..e4a8013fa 100644 --- a/fusee/fusee-secondary/src/emummc_cfg.h +++ b/fusee/fusee-secondary/src/emummc_cfg.h @@ -91,6 +91,12 @@ typedef enum { FS_VER_11_0_0, FS_VER_11_0_0_EXFAT, + FS_VER_12_0_0, + FS_VER_12_0_0_EXFAT, + + FS_VER_12_0_3, + FS_VER_12_0_3_EXFAT, + FS_VER_MAX, } emummc_fs_ver_t; diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index 36cca04dc..42885f8ec 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -33,6 +33,7 @@ #define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u) #define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u) #define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u) +#define EXOSPHERE_FLAG_FORCE_ENABLE_USB_30 (1 << 7u) #define EXOSPHERE_LOG_FLAG_INVERTED (1 << 0u) diff --git a/fusee/fusee-secondary/src/fuse.c b/fusee/fusee-secondary/src/fuse.c index efcbdba06..25fac1025 100644 --- a/fusee/fusee-secondary/src/fuse.c +++ b/fusee/fusee-secondary/src/fuse.c @@ -348,6 +348,7 @@ uint32_t fuse_get_regulator(void) { static const uint32_t fuse_version_increment_firmwares[] = { + ATMOSPHERE_TARGET_FIRMWARE_12_0_2, ATMOSPHERE_TARGET_FIRMWARE_11_0_0, ATMOSPHERE_TARGET_FIRMWARE_10_0_0, ATMOSPHERE_TARGET_FIRMWARE_9_1_0, diff --git a/fusee/fusee-secondary/src/ips.c b/fusee/fusee-secondary/src/ips.c index 2f17730ed..5b19c03b8 100644 --- a/fusee/fusee-secondary/src/ips.c +++ b/fusee/fusee-secondary/src/ips.c @@ -426,6 +426,12 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = { "\xE3\x99\x15\x6E\x84\x4E\xB0\xAA", /* FS_VER_11_0_0 */ "\x0B\xA1\x5B\xB3\x04\xB5\x05\x63", /* FS_VER_11_0_0_EXFAT */ + + "\xDC\x2A\x08\x49\x96\xBB\x3C\x01", /* FS_VER_12_0_0 */ + "\xD5\xA5\xBF\x36\x64\x0C\x49\xEA", /* FS_VER_12_0_0_EXFAT */ + + "\xC8\x67\x62\xBE\x19\xA5\x1F\xA0", /* FS_VER_12_0_3 */ + "\xE1\xE8\xD3\xD6\xA2\xFE\x0B\x10", /* FS_VER_12_0_3_EXFAT */ }; kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) { diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index 6678f8008..56fbe0882 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -568,6 +568,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv)[] = {0xA9B */ static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_send)[] = {0xE0, 0x03, 0x15, 0xAA, 0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x88, 0x4A, 0x3C, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9BF2FEA, 0xF94043EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + /* stp x10, x11, [sp, #-0x10]! ldr x11, [sp, #0xE0] @@ -596,6 +597,63 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9B static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_recv)[] = {0x08, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x18, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x7F, 0x40, 0xF9, 0x09, 0xFC, 0x60, 0xD3, 0xEA, 0x5B, 0x40, 0xF9}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0x98] + mov w10, #3 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x22] + ldr x8, [x8, #0x38] + mov x0, x22 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send)[] = {0xE0, 0x03, 0x16, 0xAA, 0xC8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xA8, 0x4A, 0x3B, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_send)[] = {0xA9BF2FEA, 0xF9404FEB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002C8, 0xF9401D08, 0xAA1603E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0xE0] + mov w10, #3 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x28] + ldr x8, [x8, #0x38] + mov x0, x28 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv)[] = {0x88, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x1C, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x08, 0x4B, 0x3A, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400388, 0xF9401D08, 0xAA1C03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + + /* svcControlCodeMemory Patches */ /* b.eq -> nop */ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP}; @@ -605,6 +663,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[ static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, svc_control_codememory)[] = {MAKE_NOP}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(500, system_memory_increase)[] = {0x52A3C008}; /* MOV W8, #0x1E000000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */ @@ -613,6 +672,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, system_memory_increase)[ static const instruction_t MAKE_KERNEL_PATCH_NAME(900, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */ +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */ /* Hook Definitions. */ static const kernel_patch_t g_kernel_patches_100[] = { @@ -935,6 +995,35 @@ static const kernel_patch_t g_kernel_patches_1101[] = { } }; +static const kernel_patch_t g_kernel_patches_1200[] = { + { /* Send Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_send))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_send) + }, + { /* Receive Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv) + }, + { /* svcControlCodeMemory Patch. */ + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory))/sizeof(instruction_t), + .payload = MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory), + .patch_offset = 0x2FCB4, + }, + { /* System Memory Increase Patch. */ + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase))/sizeof(instruction_t), + .payload = MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase), + .patch_offset = 0x4809C, + } +}; + #define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers, /* Kernel Infos. */ @@ -1038,6 +1127,15 @@ static const kernel_info_t g_kernel_infos[] = { .embedded_ini_ptr = 0x180, .free_code_space_offset = 0x49EE8, KERNEL_PATCHES(1101) + }, + { /* 12.0.0. */ + .hash = {0x8D, 0x4A, 0x1E, 0xFC, 0xCC, 0x6C, 0xFE, 0x6C, 0x45, 0x14, 0x13, 0xA1, 0x7F, 0xF6, 0xDF, 0xFD, 0x7E, 0x5D, 0xD1, 0x38, 0xCE, 0x86, 0x11, 0x8B, 0x58, 0x5F, 0x89, 0x67, 0x84, 0x48, 0xA8, 0x17, }, + .hash_offset = 0x1C4, + .hash_size = 0x68000 - 0x1C4, + .embedded_ini_offset = 0x68000, + .embedded_ini_ptr = 0x180, + .free_code_space_offset = 0x48810, + KERNEL_PATCHES(1200) } }; diff --git a/fusee/fusee-secondary/src/key_derivation.c b/fusee/fusee-secondary/src/key_derivation.c index 8996f2da8..e5700f14e 100644 --- a/fusee/fusee-secondary/src/key_derivation.c +++ b/fusee/fusee-secondary/src/key_derivation.c @@ -62,7 +62,7 @@ static const uint8_t AL16 new_master_kek_seeds[MASTERKEY_REVISION_700_800 - MAST }; static const uint8_t AL16 master_kek_seed_mariko[0x10] = { /* TODO: Update on next change of keys. */ - 0x0E, 0x44, 0x0C, 0xED, 0xB4, 0x36, 0xC0, 0x3F, 0xAA, 0x1D, 0xAE, 0xBF, 0x62, 0xB1, 0x09, 0x82, /* Mariko MasterKek seed 0A. */ + 0xE5, 0x41, 0xAC, 0xEC, 0xD1, 0xA7, 0xD1, 0xAB, 0xED, 0x03, 0x77, 0xF1, 0x27, 0xCA, 0xF8, 0xF1, /* Mariko MasterKek seed 0B. */ }; static nx_dec_keyblob_t AL16 g_dec_keyblobs[32]; @@ -153,7 +153,7 @@ int derive_nx_keydata_erista(uint32_t target_firmware, const nx_keyblob_t *keybl if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { /* NOTE: We load in the current key for all >= 8.1.0 firmwares to reduce sept binaries. */ - desired_keyblob = MASTERKEY_REVISION_910_CURRENT; + desired_keyblob = MASTERKEY_REVISION_C10_CURRENT; } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { desired_keyblob = MASTERKEY_REVISION_700_800; } else { diff --git a/fusee/fusee-secondary/src/masterkey.c b/fusee/fusee-secondary/src/masterkey.c index 94a1c8b4e..2063484f2 100644 --- a/fusee/fusee-secondary/src/masterkey.c +++ b/fusee/fusee-secondary/src/masterkey.c @@ -44,6 +44,7 @@ static const uint8_t mkey_vectors_dev[MASTERKEY_REVISION_MAX][0x10] = { {0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */ {0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */ {0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */ + {0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C}, /* Master key 0A encrypted with Master key 0B. */ }; /* Retail unit keys. */ @@ -59,17 +60,19 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = { {0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */ {0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */ {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */ + {0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98}, /* Master key 0A encrypted with Master key 0B. */ }; static const uint8_t new_device_key_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { - {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.x New Device Key Source. */ - {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.x New Device Key Source. */ - {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */ - {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */ - {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */ - {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 New Device Key Source. */ - {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 New Device Key Source. */ - {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 New Device Key Source. */ + {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.x New Device Key Source. */ + {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.x New Device Key Source. */ + {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */ + {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */ + {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */ + {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 New Device Key Source. */ + {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 New Device Key Source. */ + {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 New Device Key Source. */ + {0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6}, /* 12.1.0 New Device Key Source. */ }; static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { @@ -80,7 +83,8 @@ static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x {0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 New Device Keygen Source. */ {0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 New Device Keygen Source. */ {0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 New Device Keygen Source. */ - {0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ + {0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 New Device Keygen Source. */ + {0xC2, 0x65, 0x34, 0x6E, 0xC7, 0xC6, 0x5D, 0x97, 0x3E, 0x34, 0x5C, 0x6B, 0xB3, 0x7E, 0xC6, 0xE3}, /* 12.1.0 New Device Keygen Source. */ }; static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { @@ -91,7 +95,8 @@ static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS {0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 New Device Keygen Source. */ {0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 New Device Keygen Source. */ {0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 New Device Keygen Source. */ - {0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B}, /* 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ + {0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B}, /* 9.1.0 New Device Keygen Source. */ + {0xC4, 0xBB, 0xF3, 0x9F, 0xA3, 0xAA, 0x00, 0x99, 0x7C, 0x97, 0xAD, 0x91, 0x8F, 0xE8, 0x45, 0xCB}, /* 12.1.0 New Device Keygen Source. */ }; /* Determine the current SoC for Mariko specific code. */ diff --git a/fusee/fusee-secondary/src/masterkey.h b/fusee/fusee-secondary/src/masterkey.h index 0003c189a..333df6e72 100644 --- a/fusee/fusee-secondary/src/masterkey.h +++ b/fusee/fusee-secondary/src/masterkey.h @@ -19,8 +19,8 @@ /* This is glue code to enable master key support across versions. */ -/* TODO: Update to 0xC on release of new master key. */ -#define MASTERKEY_REVISION_MAX 0xB +/* TODO: Update to 0xD on release of new master key. */ +#define MASTERKEY_REVISION_MAX 0xC #define MASTERKEY_REVISION_100_230 0x00 #define MASTERKEY_REVISION_300 0x01 @@ -32,7 +32,8 @@ #define MASTERKEY_REVISION_700_800 0x07 #define MASTERKEY_REVISION_810 0x08 #define MASTERKEY_REVISION_900 0x09 -#define MASTERKEY_REVISION_910_CURRENT 0x0A +#define MASTERKEY_REVISION_910_1203 0x0A +#define MASTERKEY_REVISION_C10_CURRENT 0x0B #define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410) diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index e63fa0950..314582f89 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -256,6 +256,24 @@ static int stratosphere_ini_handler(void *user, const char *section, const char return 1; } +static int system_settings_ini_handler(void *user, const char *section, const char *name, const char *value) { + uint32_t *flags = (uint32_t *)user; + if (strcmp(section, "usb") == 0) { + if (strcmp(name, "usb30_force_enabled") == 0) { + if (strcmp(value, "u8!0x1") == 0) { + *flags |= EXOSPHERE_FLAG_FORCE_ENABLE_USB_30; + } else if (strcmp(value, "u8!0x0") == 0) { + *flags &= ~(EXOSPHERE_FLAG_FORCE_ENABLE_USB_30); + } + } else { + return 0; + } + } else { + return 0; + } + return 1; +} + static bool is_nca_present(const char *nca_name) { char path[0x100]; snprintf(path, sizeof(path), "system:/contents/registered/%s.nca", nca_name); @@ -267,7 +285,15 @@ static bool is_nca_present(const char *nca_name) { static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_1_0) { + CHECK_NCA("9d9d83d68d9517f245f3e8cd7f93c416", 12_1_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_2) { + CHECK_NCA("a1863a5c0e1cedd442f5e60b0422dc15", 12_0_3); + CHECK_NCA("63d928b5a3016fe8cc0e76d2f06f4e98", 12_0_2); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_0) { + CHECK_NCA("e65114b456f9d0b566a80e53bade2d89", 12_0_1); + CHECK_NCA("bd4185843550fbba125b20787005d1d2", 12_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) { CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1); CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0); } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { @@ -370,6 +396,12 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) { return ATMOSPHERE_TARGET_FIRMWARE_10_0_0; } else if (memcmp(package1loader_header->build_timestamp, "20201030", 8) == 0) { return ATMOSPHERE_TARGET_FIRMWARE_11_0_0; + } else if (memcmp(package1loader_header->build_timestamp, "20210129", 8) == 0) { + return ATMOSPHERE_TARGET_FIRMWARE_12_0_0; + } else if (memcmp(package1loader_header->build_timestamp, "20210422", 8) == 0) { + return ATMOSPHERE_TARGET_FIRMWARE_12_0_2; + } else if (memcmp(package1loader_header->build_timestamp, "20210607", 8) == 0) { + return ATMOSPHERE_TARGET_FIRMWARE_12_1_0; } else { fatal_error("[NXBOOT] Unable to identify package1!\n"); } @@ -537,6 +569,15 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke /* Apply lcd vendor. */ exo_cfg.lcd_vendor = display_get_lcd_vendor(); + /* Read and parse system settings.ini to determine usb 3.0 enable. */ + char *settings_ini = calloc(1, 0x20000); + if (read_from_file(settings_ini, 0x1FFFF, "atmosphere/config/system_settings.ini")) { + if (ini_parse_string(settings_ini, system_settings_ini_handler, &exo_cfg.flags[0]) < 0) { + fatal_error("[NXBOOT] Failed to parse system_settings.ini!\n"); + } + } + free(settings_ini); + if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) { fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n"); } @@ -568,6 +609,15 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) { if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0 && !(fuse_get_reserved_odm(7) & ~0x00001FFF)) { kip_patches_set_enable_nogc(); } + + /* NOTE: 12.0.0 added a new lotus firmware, but did not burn a fuse. */ + /* This is literally undetectable using normal fuses.... */ + /* C'est la vie. */ + + /* Check if the fuses are < 12.0.0, but firmware is >= 12.0.0 */ + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_2 && !(fuse_get_reserved_odm(7) & ~0x00003FFF)) { + kip_patches_set_enable_nogc(); + } } } diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c index 5b7b80b9d..744b38d71 100644 --- a/fusee/fusee-secondary/src/package2.c +++ b/fusee/fusee-secondary/src/package2.c @@ -250,7 +250,7 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[] /* Perform version checks. */ /* We will be compatible with all package2s released before current, but not newer ones. */ - if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1100_CURRENT) { + if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1202_CURRENT) { return true; } diff --git a/fusee/fusee-secondary/src/package2.h b/fusee/fusee-secondary/src/package2.h index e6a073fa7..4347b41f1 100644 --- a/fusee/fusee-secondary/src/package2.h +++ b/fusee/fusee-secondary/src/package2.h @@ -41,7 +41,8 @@ #define PACKAGE2_MAXVER_900 0xC #define PACKAGE2_MAXVER_910_920 0xD #define PACKAGE2_MAXVER_1000 0xE -#define PACKAGE2_MAXVER_1100_CURRENT 0xF +#define PACKAGE2_MAXVER_1100 0xF +#define PACKAGE2_MAXVER_1202_CURRENT 0x10 #define PACKAGE2_MINVER_100 0x3 #define PACKAGE2_MINVER_200 0x4 @@ -56,7 +57,8 @@ #define PACKAGE2_MINVER_900 0xD #define PACKAGE2_MINVER_910_920 0xE #define PACKAGE2_MINVER_1000 0xF -#define PACKAGE2_MINVER_1100_CURRENT 0x10 +#define PACKAGE2_MINVER_1100 0x10 +#define PACKAGE2_MINVER_1202_CURRENT 0x11 #define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull)) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index d525598ae..e968da026 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = 17960517bad5d2d07effb28b744ac8d907d571e0 - parent = ee2e9d50fd93721b365daf0eae1eef17c8ba62e8 + commit = e96b24f842a29e04c745d83fca477dbd55f20506 + parent = ea7b6e14f9c94d85036bcf967ec85312eeb6fd11 method = merge cmdver = 0.4.1 diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 2146502c4..9e6448df9 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -18,7 +18,7 @@ endif ATMOSPHERE_BUILD_SETTINGS ?= export ATMOSPHERE_DEFINES := -DATMOSPHERE -export ATMOSPHERE_SETTINGS := -fPIE -g $(ATMOSPHERE_BUILD_SETTINGS) +export ATMOSPHERE_SETTINGS := -fPIE -g -gdwarf-4 $(ATMOSPHERE_BUILD_SETTINGS) export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector \ -Wno-format-truncation -Wno-format-zero-length -Wno-stringop-truncation diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk index 6387d8989..91d3ce84d 100644 --- a/libraries/config/templates/exosphere.mk +++ b/libraries/config/templates/exosphere.mk @@ -8,19 +8,25 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk #--------------------------------------------------------------------------------- ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm) DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) endif -export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-exceptions -fno-rtti -fno-use-cxa-atexit -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-exceptions -fno-rtti -fno-use-cxa-atexit -nostdlib -nostartfiles -g -gdwarf-4 $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,__cxa_throw \ diff --git a/libraries/config/templates/mesosphere.mk b/libraries/config/templates/mesosphere.mk index 142dfc852..c55a23dbf 100644 --- a/libraries/config/templates/mesosphere.mk +++ b/libraries/config/templates/mesosphere.mk @@ -12,7 +12,7 @@ export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) -export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -nostdlib -nostartfiles -g -gdwarf-4 $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,__cxa_throw \ diff --git a/libraries/config/templates/stratosphere.mk b/libraries/config/templates/stratosphere.mk index 6f4395b1f..fe7964907 100644 --- a/libraries/config/templates/stratosphere.mk +++ b/libraries/config/templates/stratosphere.mk @@ -34,7 +34,8 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,_Unwind_Resume \ -Wl,--wrap,_ZSt19__throw_logic_errorPKc \ -Wl,--wrap,_ZSt20__throw_length_errorPKc \ - -Wl,--wrap,_ZNSt11logic_errorC2EPKc + -Wl,--wrap,_ZNSt11logic_errorC2EPKc \ + -Wl,--wrap,exit export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map) diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk index 3b3abcd84..35592ce4b 100644 --- a/libraries/libexosphere/arm.mk +++ b/libraries/libexosphere/arm.mk @@ -16,7 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) @@ -93,6 +96,8 @@ clean-$(strip $1): @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done +.PHONY: $(strip $1) clean-$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + endef $(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ @@ -114,7 +119,7 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) -.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) +.PHONY: clean all $(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk index 624a28bb5..78e2cc540 100644 --- a/libraries/libexosphere/arm64.mk +++ b/libraries/libexosphere/arm64.mk @@ -16,7 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions \ + -Wno-array-bounds \ + -Wno-stringop-overflow \ + -Wno-stringop-overread CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) @@ -93,6 +96,8 @@ clean-$(strip $1): @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done +.PHONY: $(strip $1) clean-$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + endef $(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ @@ -114,7 +119,7 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) -.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) +.PHONY: clean all $(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index cf8b54e9d..3c042db84 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -51,9 +51,9 @@ namespace ams::fuse { DramId_IcosaSamsung4GB = 0, DramId_IcosaHynix4GB = 1, DramId_IcosaMicron4GB = 2, - DramId_AulaHynix1y4GB = 3, + DramId_IowaHynix1y4GB = 3, DramId_IcosaSamsung6GB = 4, - DramId_CopperHynix4GB = 5, + DramId_HoagHynix1y4GB = 5, DramId_CopperMicron4GB = 6, DramId_IowaX1X2Samsung4GB = 7, DramId_IowaSansung4GB = 8, diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp index 5e58876f1..dc4216097 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp @@ -20,17 +20,18 @@ namespace ams::pkg1 { enum KeyGeneration : int { - KeyGeneration_1_0_0 = 0x00, - KeyGeneration_3_0_0 = 0x01, - KeyGeneration_3_0_1 = 0x02, - KeyGeneration_4_0_0 = 0x03, - KeyGeneration_5_0_0 = 0x04, - KeyGeneration_6_0_0 = 0x05, - KeyGeneration_6_2_0 = 0x06, - KeyGeneration_7_0_0 = 0x07, - KeyGeneration_8_1_0 = 0x08, - KeyGeneration_9_0_0 = 0x09, - KeyGeneration_9_1_0 = 0x0A, + KeyGeneration_1_0_0 = 0x00, + KeyGeneration_3_0_0 = 0x01, + KeyGeneration_3_0_1 = 0x02, + KeyGeneration_4_0_0 = 0x03, + KeyGeneration_5_0_0 = 0x04, + KeyGeneration_6_0_0 = 0x05, + KeyGeneration_6_2_0 = 0x06, + KeyGeneration_7_0_0 = 0x07, + KeyGeneration_8_1_0 = 0x08, + KeyGeneration_9_0_0 = 0x09, + KeyGeneration_9_1_0 = 0x0A, + KeyGeneration_12_1_0 = 0x0B, KeyGeneration_Count, diff --git a/libraries/libexosphere/include/exosphere/pkg2.hpp b/libraries/libexosphere/include/exosphere/pkg2.hpp index b96a82b12..150160c94 100644 --- a/libraries/libexosphere/include/exosphere/pkg2.hpp +++ b/libraries/libexosphere/include/exosphere/pkg2.hpp @@ -23,8 +23,8 @@ namespace ams::pkg2 { constexpr inline int PayloadCount = 3; - constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x10 in Nintendo's code. */ - constexpr inline int CurrentBootloaderVersion = 0xE; + constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x13 in Nintendo's code. */ + constexpr inline int CurrentBootloaderVersion = 0xF; struct Package2Meta { using Magic = util::FourCC<'P','K','2','1'>; diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 6c974b1c7..1bfcfb955 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -29,6 +29,7 @@ namespace ams::secmon { SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4), SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), + SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7), SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, }; @@ -101,6 +102,7 @@ namespace ams::secmon { constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags[0] & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags[0] & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + constexpr bool IsUsb30ForceEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ForceEnableUsb30) != 0; } constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); } }; diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index 40047a9bb..ba9157643 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -165,6 +165,7 @@ namespace ams::fuse { } constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = { + TargetFirmware_12_0_2, TargetFirmware_11_0_0, TargetFirmware_10_0_0, TargetFirmware_9_1_0, diff --git a/libraries/libmesosphere/Makefile b/libraries/libmesosphere/Makefile index 18027ae78..3bc92c36c 100644 --- a/libraries/libmesosphere/Makefile +++ b/libraries/libmesosphere/Makefile @@ -88,6 +88,8 @@ clean-$(strip $1): @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done +.PHONY: $(strip $1) clean-$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + endef $(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ @@ -109,7 +111,7 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) -.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) +.PHONY: clean all $(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ @@ -140,6 +142,8 @@ $(OFILES_SRC) : $(HFILES_BIN) kern_libc_generic.o: CFLAGS += -fno-builtin +kern_k_auto_object.o: CXXFLAGS += -fno-lto + #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin #--------------------------------------------------------------------------------- diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp index 04abbd3da..c167e735b 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp @@ -14,10 +14,11 @@ * along with this program. If not, see . */ #pragma once +#include namespace ams::kern::init { - struct KInitArguments { + struct alignas(util::CeilingPowerOfTwo(INIT_ARGUMENTS_SIZE)) KInitArguments { u64 ttbr0; u64 ttbr1; u64 tcr; @@ -31,5 +32,20 @@ namespace ams::kern::init { u64 setup_function; u64 exception_stack; }; + static_assert(alignof(KInitArguments) == util::CeilingPowerOfTwo(INIT_ARGUMENTS_SIZE)); + static_assert(sizeof(KInitArguments) == std::max(INIT_ARGUMENTS_SIZE, util::CeilingPowerOfTwo(INIT_ARGUMENTS_SIZE))); + + static_assert(__builtin_offsetof(KInitArguments, ttbr0) == INIT_ARGUMENTS_TTBR0); + static_assert(__builtin_offsetof(KInitArguments, ttbr1) == INIT_ARGUMENTS_TTBR1); + static_assert(__builtin_offsetof(KInitArguments, tcr) == INIT_ARGUMENTS_TCR); + static_assert(__builtin_offsetof(KInitArguments, mair) == INIT_ARGUMENTS_MAIR); + static_assert(__builtin_offsetof(KInitArguments, cpuactlr) == INIT_ARGUMENTS_CPUACTLR); + static_assert(__builtin_offsetof(KInitArguments, cpuectlr) == INIT_ARGUMENTS_CPUECTLR); + static_assert(__builtin_offsetof(KInitArguments, sctlr) == INIT_ARGUMENTS_SCTLR); + static_assert(__builtin_offsetof(KInitArguments, sp) == INIT_ARGUMENTS_SP); + static_assert(__builtin_offsetof(KInitArguments, entrypoint) == INIT_ARGUMENTS_ENTRYPOINT); + static_assert(__builtin_offsetof(KInitArguments, argument) == INIT_ARGUMENTS_ARGUMENT); + static_assert(__builtin_offsetof(KInitArguments, setup_function) == INIT_ARGUMENTS_SETUP_FUNCTION); + static_assert(__builtin_offsetof(KInitArguments, exception_stack) == INIT_ARGUMENTS_EXCEPTION_STACK); } \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp index 21fbb28b5..ea53fca94 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp @@ -38,37 +38,67 @@ namespace ams::kern::arch::arm64::init { public: class IPageAllocator { public: - virtual KPhysicalAddress Allocate() { return Null; } - virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); } + virtual KPhysicalAddress Allocate(size_t size) = 0; + virtual void Free(KPhysicalAddress phys_addr, size_t size) = 0; }; - - struct NoClear{}; private: - KPhysicalAddress m_l1_table; + KPhysicalAddress m_l1_tables[2]; + u32 m_num_entries[2]; + public: - constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1, NoClear) : m_l1_table(l1) { /* ... */ } + KInitialPageTable(KVirtualAddress start_address, KVirtualAddress end_address, IPageAllocator &allocator) { + /* Set tables. */ + m_l1_tables[0] = AllocateNewPageTable(allocator); + m_l1_tables[1] = AllocateNewPageTable(allocator); - constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : KInitialPageTable(l1, NoClear{}) { - ClearNewPageTable(m_l1_table); + /* Set counts. */ + m_num_entries[0] = MaxPageTableEntries; + m_num_entries[1] = ((end_address / L1BlockSize) & (MaxPageTableEntries - 1)) - ((start_address / L1BlockSize) & (MaxPageTableEntries - 1)) + 1; } - constexpr ALWAYS_INLINE uintptr_t GetL1TableAddress() const { - return GetInteger(m_l1_table); + KInitialPageTable() { + /* Set tables. */ + m_l1_tables[0] = util::AlignDown(cpu::GetTtbr0El1(), PageSize); + m_l1_tables[1] = util::AlignDown(cpu::GetTtbr1El1(), PageSize); + + /* Set counts. */ + cpu::TranslationControlRegisterAccessor tcr; + m_num_entries[0] = tcr.GetT0Size() / L1BlockSize; + m_num_entries[1] = tcr.GetT1Size() / L1BlockSize; + + /* Check counts. */ + MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[0] && m_num_entries[0] <= MaxPageTableEntries); + MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[1] && m_num_entries[1] <= MaxPageTableEntries); + } + + constexpr ALWAYS_INLINE uintptr_t GetTtbr0L1TableAddress() const { + return GetInteger(m_l1_tables[0]); + } + + constexpr ALWAYS_INLINE uintptr_t GetTtbr1L1TableAddress() const { + return GetInteger(m_l1_tables[1]); } private: - static constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KPhysicalAddress _l1_table, KVirtualAddress address) { - L1PageTableEntry *l1_table = reinterpret_cast(GetInteger(_l1_table)); - return l1_table + ((GetInteger(address) >> 30) & (MaxPageTableEntries - 1)); + constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KVirtualAddress address) const { + const size_t index = (GetInteger(address) >> (BITSIZEOF(address) - 1)) & 1; + L1PageTableEntry *l1_table = reinterpret_cast(GetInteger(m_l1_tables[index])); + return l1_table + ((GetInteger(address) / L1BlockSize) & (m_num_entries[index] - 1)); } static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address) { L2PageTableEntry *l2_table = reinterpret_cast(GetInteger(entry->GetTable())); - return l2_table + ((GetInteger(address) >> 21) & (MaxPageTableEntries - 1)); + return l2_table + ((GetInteger(address) / L2BlockSize) & (MaxPageTableEntries - 1)); } static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address) { L3PageTableEntry *l3_table = reinterpret_cast(GetInteger(entry->GetTable())); - return l3_table + ((GetInteger(address) >> 12) & (MaxPageTableEntries - 1)); + return l3_table + ((GetInteger(address) / L3BlockSize) & (MaxPageTableEntries - 1)); + } + + static ALWAYS_INLINE KPhysicalAddress AllocateNewPageTable(IPageAllocator &allocator) { + auto address = allocator.Allocate(PageSize); + ClearNewPageTable(address); + return address; } static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) { @@ -83,7 +113,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; size_t count = 0; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped or we're empty, advance by L1BlockSize. */ if (l1_entry->IsBlock() || l1_entry->IsEmpty()) { @@ -137,7 +167,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; size_t count = 0; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped or we're empty, advance by L1BlockSize. */ if (l1_entry->IsBlock() || l1_entry->IsEmpty()) { @@ -194,7 +224,7 @@ namespace ams::kern::arch::arm64::init { } PageTableEntry *GetMappingEntry(KVirtualAddress virt_addr, size_t block_size) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); if (l1_entry->IsBlock()) { MESOSPHERE_INIT_ABORT_UNLESS(block_size == L1BlockSize); @@ -301,7 +331,7 @@ namespace ams::kern::arch::arm64::init { /* Iteratively map pages until the requested region is mapped. */ while (size > 0) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* Can we make an L1 block? */ if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && size >= L1BlockSize) { @@ -316,7 +346,7 @@ namespace ams::kern::arch::arm64::init { /* If we don't already have an L2 table, we need to make a new one. */ if (!l1_entry->IsTable()) { - KPhysicalAddress new_table = allocator.Allocate(); + KPhysicalAddress new_table = AllocateNewPageTable(allocator); ClearNewPageTable(new_table); *l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever()); cpu::DataSynchronizationBarrierInnerShareable(); @@ -350,7 +380,7 @@ namespace ams::kern::arch::arm64::init { /* If we don't already have an L3 table, we need to make a new one. */ if (!l2_entry->IsTable()) { - KPhysicalAddress new_table = allocator.Allocate(); + KPhysicalAddress new_table = AllocateNewPageTable(allocator); ClearNewPageTable(new_table); *l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever()); cpu::DataSynchronizationBarrierInnerShareable(); @@ -382,7 +412,7 @@ namespace ams::kern::arch::arm64::init { KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const { /* Get the L1 entry. */ - const L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + const L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); if (l1_entry->IsBlock()) { return l1_entry->GetBlock() + (GetInteger(virt_addr) & (L1BlockSize - 1)); @@ -444,7 +474,7 @@ namespace ams::kern::arch::arm64::init { }; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped, update. */ if (l1_entry->IsBlock()) { @@ -485,7 +515,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped, the address isn't free. */ if (l1_entry->IsBlock()) { @@ -534,7 +564,7 @@ namespace ams::kern::arch::arm64::init { /* Iteratively reprotect pages until the requested region is reprotected. */ while (size > 0) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* Check if an L1 block is present. */ if (l1_entry->IsBlock()) { @@ -673,11 +703,18 @@ namespace ams::kern::arch::arm64::init { }; - class KInitialPageAllocator : public KInitialPageTable::IPageAllocator { + class KInitialPageAllocator final : public KInitialPageTable::IPageAllocator { + private: + static constexpr inline size_t FreeUnitSize = BITSIZEOF(u64) * PageSize; + struct FreeListEntry { + FreeListEntry *next; + size_t size; + }; public: struct State { - uintptr_t next_address; - uintptr_t free_bitmap; + uintptr_t start_address; + uintptr_t end_address; + FreeListEntry *free_head; }; private: State m_state; @@ -685,8 +722,8 @@ namespace ams::kern::arch::arm64::init { constexpr ALWAYS_INLINE KInitialPageAllocator() : m_state{} { /* ... */ } ALWAYS_INLINE void Initialize(uintptr_t address) { - m_state.next_address = address + BITSIZEOF(m_state.free_bitmap) * PageSize; - m_state.free_bitmap = ~uintptr_t(); + m_state.start_address = address; + m_state.end_address = address; } ALWAYS_INLINE void InitializeFromState(uintptr_t state_val) { @@ -697,28 +734,134 @@ namespace ams::kern::arch::arm64::init { *out = m_state; m_state = {}; } - public: - virtual KPhysicalAddress Allocate() override { - MESOSPHERE_INIT_ABORT_UNLESS(m_state.next_address != Null); - uintptr_t allocated = m_state.next_address; - if (m_state.free_bitmap != 0) { - u64 index; - uintptr_t mask; - do { - index = KSystemControl::Init::GenerateRandomRange(0, BITSIZEOF(m_state.free_bitmap) - 1); - mask = (static_cast(1) << index); - } while ((m_state.free_bitmap & mask) == 0); - m_state.free_bitmap &= ~mask; - allocated = m_state.next_address - ((BITSIZEOF(m_state.free_bitmap) - index) * PageSize); - } else { - m_state.next_address += PageSize; + private: + bool CanAllocate(size_t align, size_t size) const { + for (auto *cur = m_state.free_head; cur != nullptr; cur = cur->next) { + const uintptr_t cur_last = reinterpret_cast(cur) + cur->size - 1; + const uintptr_t alloc_last = util::AlignUp(reinterpret_cast(cur), align) + size - 1; + if (alloc_last <= cur_last) { + return true; + } } - - ClearPhysicalMemory(allocated, PageSize); - return allocated; + return false; } - /* No need to override free. The default does nothing, and so would we. */ + bool TryAllocate(uintptr_t address, size_t size) { + /* Try to allocate the region. */ + auto **prev_next = std::addressof(m_state.free_head); + for (auto *cur = m_state.free_head; cur != nullptr; prev_next = std::addressof(cur->next), cur = cur->next) { + const uintptr_t cur_start = reinterpret_cast(cur); + const uintptr_t cur_last = cur_start + cur->size - 1; + if (cur_start <= address && address + size - 1 <= cur_last) { + auto *alloc = reinterpret_cast(address); + + /* Perform fragmentation at front. */ + if (cur != alloc) { + prev_next = std::addressof(cur->next); + *alloc = { + .next = cur->next, + .size = cur_start + cur->size - address, + }; + *cur = { + .next = alloc, + .size = address - cur_start, + }; + } + + /* Perform fragmentation at tail. */ + if (alloc->size != size) { + auto *next = reinterpret_cast(address + size); + *next = { + .next = alloc->next, + .size = alloc->size - size, + }; + *alloc = { + .next = next, + .size = size, + }; + } + + *prev_next = alloc->next; + return true; + } + } + + return false; + } + public: + KPhysicalAddress Allocate(size_t align, size_t size) { + /* Ensure that the free list is non-empty. */ + while (!this->CanAllocate(align, size)) { + this->Free(m_state.end_address, FreeUnitSize); + m_state.end_address += FreeUnitSize; + } + + /* Allocate a random address. */ + const uintptr_t aligned_start = util::AlignUp(m_state.start_address, align); + const uintptr_t aligned_end = util::AlignDown(m_state.end_address, align); + const size_t ind_max = ((aligned_end - aligned_start) / align) - 1; + while (true) { + if (const uintptr_t random_address = aligned_start + (KSystemControl::Init::GenerateRandomRange(0, ind_max) * align); this->TryAllocate(random_address, size)) { + return random_address; + } + } + } + + virtual KPhysicalAddress Allocate(size_t size) override { + return this->Allocate(size, size); + } + + virtual void Free(KPhysicalAddress phys_addr, size_t size) override { + auto **prev_next = std::addressof(m_state.free_head); + auto *new_chunk = reinterpret_cast(GetInteger(phys_addr)); + if (auto *cur = m_state.free_head; cur != nullptr) { + const uintptr_t new_start = reinterpret_cast(new_chunk); + const uintptr_t new_end = GetInteger(phys_addr) + size; + while (true) { + /* Attempt coalescing. */ + const uintptr_t cur_start = reinterpret_cast(cur); + const uintptr_t cur_end = cur_start + cur->size; + if (new_start < new_end) { + if (new_end < cur_start) { + *new_chunk = { + .next = cur, + .size = size, + }; + break; + } else if (new_end == cur_start) { + *new_chunk = { + .next = cur->next, + .size = cur->size + size, + }; + break; + } + } else if (cur_end == new_start) { + cur->size += size; + return; + } + + prev_next = std::addressof(cur->next); + if (cur->next != nullptr) { + cur = cur->next; + } else { + *new_chunk = { + .next = nullptr, + .size = size, + }; + cur->next = new_chunk; + return; + } + } + + } else { + *new_chunk = { + .next = nullptr, + .size = size, + }; + } + + *prev_next = new_chunk; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h new file mode 100644 index 000000000..09f30a6ae --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +/* TODO: Different header for this? */ +#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0 + +/* ams::kern::KThread::StackParameters, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */ +#define THREAD_STACK_PARAMETERS_SIZE 0x30 +#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00 +#define THREAD_STACK_PARAMETERS_CONTEXT 0x18 +#define THREAD_STACK_PARAMETERS_CUR_THREAD 0x20 +#define THREAD_STACK_PARAMETERS_DISABLE_COUNT 0x28 +#define THREAD_STACK_PARAMETERS_DPC_FLAGS 0x2A +#define THREAD_STACK_PARAMETERS_CURRENT_SVC_ID 0x2B +#define THREAD_STACK_PARAMETERS_IS_CALLING_SVC 0x2C +#define THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER 0x2D +#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E + +/* ams::kern::arch::arm64::KThreadContext, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp */ +#define THREAD_CONTEXT_SIZE 0x290 +#define THREAD_CONTEXT_CPU_REGISTERS 0x000 +#define THREAD_CONTEXT_X19 0x000 +#define THREAD_CONTEXT_X20 0x008 +#define THREAD_CONTEXT_X21 0x010 +#define THREAD_CONTEXT_X22 0x018 +#define THREAD_CONTEXT_X23 0x020 +#define THREAD_CONTEXT_X24 0x028 +#define THREAD_CONTEXT_X25 0x030 +#define THREAD_CONTEXT_X26 0x038 +#define THREAD_CONTEXT_X27 0x040 +#define THREAD_CONTEXT_X28 0x048 +#define THREAD_CONTEXT_X29 0x050 +#define THREAD_CONTEXT_LR 0x058 +#define THREAD_CONTEXT_SP 0x060 +#define THREAD_CONTEXT_CPACR 0x068 +#define THREAD_CONTEXT_FPCR 0x070 +#define THREAD_CONTEXT_FPSR 0x078 +#define THREAD_CONTEXT_FPU_REGISTERS 0x080 +#define THREAD_CONTEXT_LOCKED 0x280 + +#define THREAD_CONTEXT_X19_X20 THREAD_CONTEXT_X19 +#define THREAD_CONTEXT_X21_X22 THREAD_CONTEXT_X21 +#define THREAD_CONTEXT_X23_X24 THREAD_CONTEXT_X23 +#define THREAD_CONTEXT_X25_X26 THREAD_CONTEXT_X25 +#define THREAD_CONTEXT_X27_X28 THREAD_CONTEXT_X27 +#define THREAD_CONTEXT_X29_X30 THREAD_CONTEXT_X29 +#define THREAD_CONTEXT_LR_SP THREAD_CONTEXT_LR +#define THREAD_CONTEXT_SP_CPACR THREAD_CONTEXT_SP +#define THREAD_CONTEXT_FPCR_FPSR THREAD_CONTEXT_FPCR + +/* ams::kern::arch::arm64::KExceptionContext, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp */ +#define EXCEPTION_CONTEXT_SIZE 0x120 +#define EXCEPTION_CONTEXT_X0 0x000 +#define EXCEPTION_CONTEXT_X1 0x008 +#define EXCEPTION_CONTEXT_X2 0x010 +#define EXCEPTION_CONTEXT_X3 0x018 +#define EXCEPTION_CONTEXT_X4 0x020 +#define EXCEPTION_CONTEXT_X5 0x028 +#define EXCEPTION_CONTEXT_X6 0x030 +#define EXCEPTION_CONTEXT_X7 0x038 +#define EXCEPTION_CONTEXT_X8 0x040 +#define EXCEPTION_CONTEXT_X9 0x048 +#define EXCEPTION_CONTEXT_X10 0x050 +#define EXCEPTION_CONTEXT_X11 0x058 +#define EXCEPTION_CONTEXT_X12 0x060 +#define EXCEPTION_CONTEXT_X13 0x068 +#define EXCEPTION_CONTEXT_X14 0x070 +#define EXCEPTION_CONTEXT_X15 0x078 +#define EXCEPTION_CONTEXT_X16 0x080 +#define EXCEPTION_CONTEXT_X17 0x088 +#define EXCEPTION_CONTEXT_X18 0x090 +#define EXCEPTION_CONTEXT_X19 0x098 +#define EXCEPTION_CONTEXT_X20 0x0A0 +#define EXCEPTION_CONTEXT_X21 0x0A8 +#define EXCEPTION_CONTEXT_X22 0x0B0 +#define EXCEPTION_CONTEXT_X23 0x0B8 +#define EXCEPTION_CONTEXT_X24 0x0C0 +#define EXCEPTION_CONTEXT_X25 0x0C8 +#define EXCEPTION_CONTEXT_X26 0x0D0 +#define EXCEPTION_CONTEXT_X27 0x0D8 +#define EXCEPTION_CONTEXT_X28 0x0E0 +#define EXCEPTION_CONTEXT_X29 0x0E8 +#define EXCEPTION_CONTEXT_X30 0x0F0 +#define EXCEPTION_CONTEXT_SP 0x0F8 +#define EXCEPTION_CONTEXT_PC 0x100 +#define EXCEPTION_CONTEXT_PSR 0x108 +#define EXCEPTION_CONTEXT_TPIDR 0x110 + +#define EXCEPTION_CONTEXT_X0_X1 EXCEPTION_CONTEXT_X0 +#define EXCEPTION_CONTEXT_X2_X3 EXCEPTION_CONTEXT_X2 +#define EXCEPTION_CONTEXT_X4_X5 EXCEPTION_CONTEXT_X4 +#define EXCEPTION_CONTEXT_X6_X7 EXCEPTION_CONTEXT_X6 +#define EXCEPTION_CONTEXT_X8_X9 EXCEPTION_CONTEXT_X8 +#define EXCEPTION_CONTEXT_X10_X11 EXCEPTION_CONTEXT_X10 +#define EXCEPTION_CONTEXT_X12_X13 EXCEPTION_CONTEXT_X12 +#define EXCEPTION_CONTEXT_X14_X15 EXCEPTION_CONTEXT_X14 +#define EXCEPTION_CONTEXT_X16_X17 EXCEPTION_CONTEXT_X16 +#define EXCEPTION_CONTEXT_X18_X19 EXCEPTION_CONTEXT_X18 +#define EXCEPTION_CONTEXT_X20_X21 EXCEPTION_CONTEXT_X20 +#define EXCEPTION_CONTEXT_X22_X23 EXCEPTION_CONTEXT_X22 +#define EXCEPTION_CONTEXT_X24_X25 EXCEPTION_CONTEXT_X24 +#define EXCEPTION_CONTEXT_X26_X27 EXCEPTION_CONTEXT_X26 +#define EXCEPTION_CONTEXT_X28_X29 EXCEPTION_CONTEXT_X28 +#define EXCEPTION_CONTEXT_X30_SP EXCEPTION_CONTEXT_X30 +#define EXCEPTION_CONTEXT_PC_PSR EXCEPTION_CONTEXT_PC + +#define EXCEPTION_CONTEXT_X9_X10 EXCEPTION_CONTEXT_X9 +#define EXCEPTION_CONTEXT_X19_X20 EXCEPTION_CONTEXT_X19 +#define EXCEPTION_CONTEXT_X21_X22 EXCEPTION_CONTEXT_X21 +#define EXCEPTION_CONTEXT_X23_X24 EXCEPTION_CONTEXT_X23 +#define EXCEPTION_CONTEXT_X25_X26 EXCEPTION_CONTEXT_X25 +#define EXCEPTION_CONTEXT_X27_X28 EXCEPTION_CONTEXT_X27 +#define EXCEPTION_CONTEXT_X29_X30 EXCEPTION_CONTEXT_X29 +#define EXCEPTION_CONTEXT_SP_PC EXCEPTION_CONTEXT_SP +#define EXCEPTION_CONTEXT_PSR_TPIDR EXCEPTION_CONTEXT_PSR + +/* ams::svc::arch::arm64::ThreadLocalRegion, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp */ +#define THREAD_LOCAL_REGION_MESSAGE_BUFFER 0x000 +#define THREAD_LOCAL_REGION_DISABLE_COUNT 0x100 +#define THREAD_LOCAL_REGION_INTERRUPT_FLAG 0x102 +#define THREAD_LOCAL_REGION_SIZE 0x200 + +/* ams::kern::init::KInitArguments, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp */ +#define INIT_ARGUMENTS_SIZE 0x60 +#define INIT_ARGUMENTS_TTBR0 0x00 +#define INIT_ARGUMENTS_TTBR1 0x08 +#define INIT_ARGUMENTS_TCR 0x10 +#define INIT_ARGUMENTS_MAIR 0x18 +#define INIT_ARGUMENTS_CPUACTLR 0x20 +#define INIT_ARGUMENTS_CPUECTLR 0x28 +#define INIT_ARGUMENTS_SCTLR 0x30 +#define INIT_ARGUMENTS_SP 0x38 +#define INIT_ARGUMENTS_ENTRYPOINT 0x40 +#define INIT_ARGUMENTS_ARGUMENT 0x48 +#define INIT_ARGUMENTS_SETUP_FUNCTION 0x50 +#define INIT_ARGUMENTS_EXCEPTION_STACK 0x58 + +/* ams::kern::KScheduler (::SchedulingState), https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp */ +/* NOTE: Due to constraints on ldarb relative offsets, KSCHEDULER_NEEDS_SCHEDULING cannot trivially be changed, and will require assembly edits. */ +#define KSCHEDULER_NEEDS_SCHEDULING 0x00 +#define KSCHEDULER_INTERRUPT_TASK_THREAD_RUNNABLE 0x01 +#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x10 +#define KSCHEDULER_IDLE_THREAD_STACK 0x18 diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp index c3c915a23..c3693bfac 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp @@ -172,10 +172,8 @@ namespace ams::kern::arch::arm64::cpu { /* Cache management helpers. */ void ClearPageToZeroImpl(void *); - void FlushEntireDataCacheSharedForInit(); - void FlushEntireDataCacheLocalForInit(); - void InvalidateEntireInstructionCacheForInit(); void StoreEntireCacheForInit(); + void FlushEntireCacheForInit(); void FlushEntireDataCache(); @@ -232,7 +230,7 @@ namespace ams::kern::arch::arm64::cpu { } ALWAYS_INLINE void SetExceptionThreadStackTop(uintptr_t top) { - SetTpidrEl1(top); + cpu::SetCntvCvalEl0(top); } ALWAYS_INLINE void SwitchThreadLocalRegion(uintptr_t tlr) { diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp index 2d4410f84..d8c936e25 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp @@ -74,6 +74,7 @@ namespace ams::kern::arch::arm64::cpu { MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntkCtlEl1, cntkctl_el1) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCtlEl0, cntp_ctl_el0) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCvalEl0, cntp_cval_el0) + MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntvCvalEl0, cntv_cval_el0) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Daif, daif) @@ -197,6 +198,11 @@ namespace ams::kern::arch::arm64::cpu { public: MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(TranslationControl, tcr_el1) + constexpr ALWAYS_INLINE size_t GetT0Size() const { + const size_t shift_value = this->GetBits(0, 6); + return size_t(1) << (size_t(64) - shift_value); + } + constexpr ALWAYS_INLINE size_t GetT1Size() const { const size_t shift_value = this->GetBits(16, 6); return size_t(1) << (size_t(64) - shift_value); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp index 40982b697..255fec7e3 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp @@ -43,6 +43,42 @@ namespace ams::kern::arch::arm64 { } } }; - static_assert(sizeof(KExceptionContext) == 0x120); + static_assert(sizeof(KExceptionContext) == EXCEPTION_CONTEXT_SIZE); + + static_assert(__builtin_offsetof(KExceptionContext, x[ 0]) == EXCEPTION_CONTEXT_X0); + static_assert(__builtin_offsetof(KExceptionContext, x[ 1]) == EXCEPTION_CONTEXT_X1); + static_assert(__builtin_offsetof(KExceptionContext, x[ 2]) == EXCEPTION_CONTEXT_X2); + static_assert(__builtin_offsetof(KExceptionContext, x[ 3]) == EXCEPTION_CONTEXT_X3); + static_assert(__builtin_offsetof(KExceptionContext, x[ 4]) == EXCEPTION_CONTEXT_X4); + static_assert(__builtin_offsetof(KExceptionContext, x[ 5]) == EXCEPTION_CONTEXT_X5); + static_assert(__builtin_offsetof(KExceptionContext, x[ 6]) == EXCEPTION_CONTEXT_X6); + static_assert(__builtin_offsetof(KExceptionContext, x[ 7]) == EXCEPTION_CONTEXT_X7); + static_assert(__builtin_offsetof(KExceptionContext, x[ 8]) == EXCEPTION_CONTEXT_X8); + static_assert(__builtin_offsetof(KExceptionContext, x[ 9]) == EXCEPTION_CONTEXT_X9); + static_assert(__builtin_offsetof(KExceptionContext, x[10]) == EXCEPTION_CONTEXT_X10); + static_assert(__builtin_offsetof(KExceptionContext, x[11]) == EXCEPTION_CONTEXT_X11); + static_assert(__builtin_offsetof(KExceptionContext, x[12]) == EXCEPTION_CONTEXT_X12); + static_assert(__builtin_offsetof(KExceptionContext, x[13]) == EXCEPTION_CONTEXT_X13); + static_assert(__builtin_offsetof(KExceptionContext, x[14]) == EXCEPTION_CONTEXT_X14); + static_assert(__builtin_offsetof(KExceptionContext, x[15]) == EXCEPTION_CONTEXT_X15); + static_assert(__builtin_offsetof(KExceptionContext, x[16]) == EXCEPTION_CONTEXT_X16); + static_assert(__builtin_offsetof(KExceptionContext, x[17]) == EXCEPTION_CONTEXT_X17); + static_assert(__builtin_offsetof(KExceptionContext, x[18]) == EXCEPTION_CONTEXT_X18); + static_assert(__builtin_offsetof(KExceptionContext, x[19]) == EXCEPTION_CONTEXT_X19); + static_assert(__builtin_offsetof(KExceptionContext, x[20]) == EXCEPTION_CONTEXT_X20); + static_assert(__builtin_offsetof(KExceptionContext, x[21]) == EXCEPTION_CONTEXT_X21); + static_assert(__builtin_offsetof(KExceptionContext, x[22]) == EXCEPTION_CONTEXT_X22); + static_assert(__builtin_offsetof(KExceptionContext, x[23]) == EXCEPTION_CONTEXT_X23); + static_assert(__builtin_offsetof(KExceptionContext, x[24]) == EXCEPTION_CONTEXT_X24); + static_assert(__builtin_offsetof(KExceptionContext, x[25]) == EXCEPTION_CONTEXT_X25); + static_assert(__builtin_offsetof(KExceptionContext, x[26]) == EXCEPTION_CONTEXT_X26); + static_assert(__builtin_offsetof(KExceptionContext, x[27]) == EXCEPTION_CONTEXT_X27); + static_assert(__builtin_offsetof(KExceptionContext, x[28]) == EXCEPTION_CONTEXT_X28); + static_assert(__builtin_offsetof(KExceptionContext, x[29]) == EXCEPTION_CONTEXT_X29); + static_assert(__builtin_offsetof(KExceptionContext, x[30]) == EXCEPTION_CONTEXT_X30); + static_assert(__builtin_offsetof(KExceptionContext, sp) == EXCEPTION_CONTEXT_SP); + static_assert(__builtin_offsetof(KExceptionContext, pc) == EXCEPTION_CONTEXT_PC); + static_assert(__builtin_offsetof(KExceptionContext, psr) == EXCEPTION_CONTEXT_PSR); + static_assert(__builtin_offsetof(KExceptionContext, tpidr) == EXCEPTION_CONTEXT_TPIDR); } \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp index 235ae00f2..204efd447 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp @@ -85,7 +85,6 @@ namespace ams::kern::arch::arm64 { NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level); NOINLINE Result UnbindHandler(s32 irq, s32 core); - NOINLINE Result ClearInterrupt(s32 irq); NOINLINE Result ClearInterrupt(s32 irq, s32 core_id); ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) { diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp index 6cd5008b3..a12533207 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp @@ -176,7 +176,7 @@ namespace ams::kern::arch::arm64 { } NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager); + NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit); Result Finalize(); private: Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 3bf4d6cee..2277a5bff 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -30,12 +30,16 @@ namespace ams::kern::arch::arm64 { m_page_table.Activate(id); } - Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { - return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager); + Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { + return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager, resource_limit); } void Finalize() { m_page_table.Finalize(); } + ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() { + return m_page_table.AcquireDeviceMapLock(); + } + Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) { return m_page_table.SetMemoryPermission(addr, size, perm); } @@ -136,26 +140,42 @@ namespace ams::kern::arch::arm64 { return m_page_table.ReadDebugMemory(buffer, address, size); } + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + return m_page_table.ReadDebugIoMemory(buffer, address, size); + } + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size) { return m_page_table.WriteDebugMemory(address, buffer, size); } - Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { - return m_page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned); + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + return m_page_table.WriteDebugIoMemory(address, buffer, size); + } + + Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + return m_page_table.LockForMapDeviceAddressSpace(address, size, perm, is_aligned); + } + + Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) { + return m_page_table.LockForUnmapDeviceAddressSpace(address, size); } Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { return m_page_table.UnlockForDeviceAddressSpace(address, size); } - Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size) { - return m_page_table.MakePageGroupForUnmapDeviceAddressSpace(out, address, size); - } - Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size) { return m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size, mapped_size); } + Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + return m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm, is_aligned); + } + + Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) { + return m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size); + } + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { return m_page_table.LockForIpcUserBuffer(out, address, size); } @@ -180,6 +200,10 @@ namespace ams::kern::arch::arm64 { return m_page_table.UnlockForCodeMemory(address, size, pg); } + Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) { + return m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size); + } + Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) { return m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr); } @@ -208,8 +232,8 @@ namespace ams::kern::arch::arm64 { return m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, test_perm, dst_state, send); } - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { - return m_page_table.CleanupForIpcServer(address, size, dst_state, server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { + return m_page_table.CleanupForIpcServer(address, size, dst_state); } Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { @@ -232,6 +256,10 @@ namespace ams::kern::arch::arm64 { return m_page_table.UnmapPhysicalMemoryUnsafe(address, size); } + Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KProcessPageTable &src_page_table, KProcessAddress src_address) { + return m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table, src_address); + } + void DumpMemoryBlocks() const { return m_page_table.DumpMemoryBlocks(); } @@ -290,6 +318,10 @@ namespace ams::kern::arch::arm64 { KBlockInfoManager *GetBlockInfoManager() { return m_page_table.GetBlockInfoManager(); } + + KPageTableBase &GetBasePageTable() { + return m_page_table; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp index d2de2c88f..e09a48790 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp @@ -79,8 +79,38 @@ namespace ams::kern::arch::arm64 { const u128 *GetFpuRegisters() const { return m_fpu_registers; } public: static void OnThreadTerminating(const KThread *thread); + public: + static consteval bool ValidateOffsets(); }; + consteval bool KThreadContext::ValidateOffsets() { + static_assert(sizeof(KThreadContext) == THREAD_CONTEXT_SIZE); + + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.registers) == THREAD_CONTEXT_CPU_REGISTERS); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x19) == THREAD_CONTEXT_X19); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x20) == THREAD_CONTEXT_X20); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x21) == THREAD_CONTEXT_X21); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x22) == THREAD_CONTEXT_X22); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x23) == THREAD_CONTEXT_X23); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x24) == THREAD_CONTEXT_X24); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x25) == THREAD_CONTEXT_X25); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x26) == THREAD_CONTEXT_X26); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x27) == THREAD_CONTEXT_X27); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x28) == THREAD_CONTEXT_X28); + static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x29) == THREAD_CONTEXT_X29); + static_assert(__builtin_offsetof(KThreadContext, m_lr) == THREAD_CONTEXT_LR); + static_assert(__builtin_offsetof(KThreadContext, m_sp) == THREAD_CONTEXT_SP); + static_assert(__builtin_offsetof(KThreadContext, m_cpacr) == THREAD_CONTEXT_CPACR); + static_assert(__builtin_offsetof(KThreadContext, m_fpcr) == THREAD_CONTEXT_FPCR); + static_assert(__builtin_offsetof(KThreadContext, m_fpsr) == THREAD_CONTEXT_FPSR); + static_assert(__builtin_offsetof(KThreadContext, m_fpu_registers) == THREAD_CONTEXT_FPU_REGISTERS); + static_assert(__builtin_offsetof(KThreadContext, m_locked) == THREAD_CONTEXT_LOCKED); + + return true; + } + static_assert(KThreadContext::ValidateOffsets()); + + void GetUserContext(ams::svc::ThreadContext *out, const KThread *thread); } \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp index 21814b198..cb5a7ffc8 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp @@ -69,8 +69,8 @@ namespace ams::kern::board::nintendo::nx { Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size); Result Detach(ams::svc::DeviceName device_name); - Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings); - Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address); + Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings); + Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address); void Unmap(KDeviceVirtualAddress device_address, size_t size) { return this->UnmapImpl(device_address, size, false); @@ -78,12 +78,11 @@ namespace ams::kern::board::nintendo::nx { private: Result MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm); - Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm); + Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned); void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force); bool IsFree(KDeviceVirtualAddress address, u64 size) const; - Result MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const; - bool Compare(const KPageGroup &pg, KDeviceVirtualAddress device_address) const; + bool Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const; public: static void Initialize(); diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index f9d99aecf..056d5f32b 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -25,6 +25,7 @@ namespace ams::kern::board::nintendo::nx { /* Initialization. */ static size_t GetIntendedMemorySize(); static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address); + static KPhysicalAddress GetInitialProcessBinaryPhysicalAddress(); static bool ShouldIncreaseThreadResourceLimit(); static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); static size_t GetApplicationPoolSize(); diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp index f6df949b8..b249592e1 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp @@ -24,6 +24,8 @@ namespace ams::kern::init { + static_assert(util::IsPowerOfTwo(alignof(KInitArguments)) && util::IsPowerOfTwo(sizeof(KInitArguments))); + KPhysicalAddress GetInitArgumentsAddress(s32 core_id); } diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp index 3d379ca03..63197ec1e 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp @@ -27,7 +27,7 @@ namespace ams::kern::init { u32 rw_end_offset; u32 bss_offset; u32 bss_end_offset; - u32 ini_load_offset; + u32 resource_offset; u32 dynamic_offset; u32 init_array_offset; u32 init_array_end_offset; diff --git a/libraries/libmesosphere/include/mesosphere/kern_common.hpp b/libraries/libmesosphere/include/mesosphere/kern_common.hpp index 091d4ba68..7e4bd00cd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_common.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_common.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace ams::kern { diff --git a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp index bc15b0b88..8be5e14b1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp @@ -29,11 +29,14 @@ namespace ams::kern { u32 reserved; }; - NOINLINE void CopyInitialProcessBinaryToKernelMemory(); + NOINLINE size_t CopyInitialProcessBinaryToKernelMemory(); NOINLINE void CreateAndRunInitialProcesses(); u64 GetInitialProcessIdMin(); u64 GetInitialProcessIdMax(); + KVirtualAddress GetInitialProcessBinaryAddress(); size_t GetInitialProcessesSecureMemorySize(); + void LoadInitialProcessBinaryHeaderDeprecated(KPhysicalAddress pool_end); + } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index f798ca806..ff8b26bcb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -69,11 +69,12 @@ namespace ams::kern { private: MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); private: + KAutoObject *m_next_closed_object; std::atomic m_ref_count; public: static KAutoObject *Create(KAutoObject *ptr); public: - constexpr ALWAYS_INLINE explicit KAutoObject() : m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } + constexpr ALWAYS_INLINE explicit KAutoObject() : m_next_closed_object(nullptr), m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); } /* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */ @@ -120,36 +121,16 @@ namespace ams::kern { } } - ALWAYS_INLINE bool Open() { - MESOSPHERE_ASSERT_THIS(); - - /* Atomically increment the reference count, only if it's positive. */ - u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); - do { - if (AMS_UNLIKELY(cur_ref_count == 0)) { - MESOSPHERE_AUDIT(cur_ref_count != 0); - return false; - } - MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1); - } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed)); - - return true; - } - - ALWAYS_INLINE void Close() { - MESOSPHERE_ASSERT_THIS(); - - /* Atomically decrement the reference count, not allowing it to become negative. */ - u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); - do { - MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0); - } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed)); - - /* If ref count hits zero, destroy the object. */ - if (cur_ref_count - 1 == 0) { - this->Destroy(); - } - } + bool Open(); + void Close(); + private: + /* NOTE: This has to be defined *after* KThread is defined. */ + /* Nintendo seems to handle this by defining Open/Close() in a cpp, but we'd like them to remain in headers. */ + /* Implementation for this will be inside kern_k_thread.hpp, so it can be ALWAYS_INLINE. */ + void ScheduleDestruction(); + public: + /* Getter, for KThread. */ + ALWAYS_INLINE KAutoObject *GetNextClosedObject() { return m_next_closed_object; } }; class KAutoObjectWithListContainer; @@ -198,7 +179,7 @@ namespace ams::kern { } } - ~KScopedAutoObject() { + ALWAYS_INLINE ~KScopedAutoObject() { if (m_obj != nullptr) { m_obj->Close(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp index d2462d0fc..a66006a9a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp @@ -47,6 +47,7 @@ namespace ams::kern { ALWAYS_INLINE s32 GetMaxSessions() const { return m_max_sessions; } bool IsLight() const; + bool IsServerClosed() const; /* Overridden virtual functions. */ virtual void Destroy() override; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp index 6f94c8f2f..0875216ac 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp @@ -23,7 +23,7 @@ namespace ams::kern { class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject); private: - TYPED_STORAGE(KPageGroup) m_page_group; + util::TypedStorage m_page_group; KProcess *m_owner; KProcessAddress m_address; KLightLock m_lock; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp index d967ea430..5a5eaaf81 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp @@ -36,7 +36,7 @@ namespace ams::kern { void Signal(uintptr_t cv_key, s32 count); Result Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout); private: - KThread *SignalImpl(KThread *thread); + void SignalImpl(KThread *thread); }; ALWAYS_INLINE void BeforeUpdatePriority(KConditionVariable::ThreadTree *tree, KThread *thread) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp index 64bd4ca98..114895a0e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp @@ -64,8 +64,11 @@ namespace ams::kern { m_page_bitmap.Initialize(management_ptr, m_count); /* Free the pages to the bitmap. */ - std::memset(GetPointer(m_address), 0, m_count * sizeof(PageBuffer)); for (size_t i = 0; i < m_count; i++) { + /* Ensure the freed page is all-zero. */ + cpu::ClearPageToZero(GetPointer(m_address) + i); + + /* Set the bit for the free page. */ m_page_bitmap.SetBit(i); } @@ -99,6 +102,9 @@ namespace ams::kern { } void Free(PageBuffer *pb) { + /* Ensure all pages in the heap are zero. */ + cpu::ClearPageToZero(pb); + /* Take the lock. */ KScopedInterruptDisable di; KScopedSpinLock lk(m_lock); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp index c7e0ad45c..7665c4de7 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp @@ -22,7 +22,7 @@ namespace ams::kern { - template + template class KDynamicSlabHeap { NON_COPYABLE(KDynamicSlabHeap); NON_MOVEABLE(KDynamicSlabHeap); @@ -97,6 +97,13 @@ namespace ams::kern { T *Allocate() { T *allocated = reinterpret_cast(this->GetImpl()->Allocate()); + /* If we successfully allocated and we should clear the node, do so. */ + if constexpr (ClearNode) { + if (AMS_LIKELY(allocated != nullptr)) { + reinterpret_cast(allocated)->next = nullptr; + } + } + /* If we fail to allocate, try to get a new page from our next allocator. */ if (AMS_UNLIKELY(allocated == nullptr)) { if (m_page_allocator != nullptr) { @@ -113,7 +120,7 @@ namespace ams::kern { if (AMS_LIKELY(allocated != nullptr)) { /* Construct the object. */ - new (allocated) T(); + std::construct_at(allocated); /* Update our tracking. */ size_t used = m_used.fetch_add(1) + 1; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp index d62b2c85e..0babf545b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp @@ -53,46 +53,29 @@ namespace ams::kern { return pack.Get(); } - class Entry { - private: - union { - struct { - u16 linear_id; - u16 type; - } info; - Entry *next_free_entry; - } m_meta; - KAutoObject *m_object; - public: - constexpr Entry() : m_meta(), m_object(nullptr) { /* ... */ } + union EntryInfo { + struct { + u16 linear_id; + u16 type; + } info; + s32 next_free_index; - constexpr ALWAYS_INLINE void SetFree(Entry *next) { - m_object = nullptr; - m_meta.next_free_entry = next; - } - - constexpr ALWAYS_INLINE void SetUsed(KAutoObject *obj, u16 linear_id, u16 type) { - m_object = obj; - m_meta.info = { linear_id, type }; - } - - constexpr ALWAYS_INLINE KAutoObject *GetObject() const { return m_object; } - constexpr ALWAYS_INLINE Entry *GetNextFreeEntry() const { return m_meta.next_free_entry; } - constexpr ALWAYS_INLINE u16 GetLinearId() const { return m_meta.info.linear_id; } - constexpr ALWAYS_INLINE u16 GetType() const { return m_meta.info.type; } + constexpr ALWAYS_INLINE u16 GetLinearId() const { return info.linear_id; } + constexpr ALWAYS_INLINE u16 GetType() const { return info.type; } + constexpr ALWAYS_INLINE s32 GetNextFreeIndex() const { return next_free_index; } }; private: - mutable KSpinLock m_lock; - Entry *m_table; - Entry *m_free_head; - Entry m_entries[MaxTableSize]; + EntryInfo m_entry_infos[MaxTableSize]; + KAutoObject *m_objects[MaxTableSize]; + s32 m_free_head_index; u16 m_table_size; u16 m_max_count; u16 m_next_linear_id; u16 m_count; + mutable KSpinLock m_lock; public: constexpr KHandleTable() : - m_lock(), m_table(nullptr), m_free_head(nullptr), m_entries(), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0) + m_entry_infos(), m_objects(), m_free_head_index(-1), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0), m_lock() { MESOSPHERE_ASSERT_THIS(); } constexpr NOINLINE Result Initialize(s32 size) { @@ -101,19 +84,18 @@ namespace ams::kern { R_UNLESS(size <= static_cast(MaxTableSize), svc::ResultOutOfMemory()); /* Initialize all fields. */ - m_table = m_entries; - m_table_size = (size <= 0) ? MaxTableSize : size; - m_next_linear_id = MinLinearId; - m_count = 0; - m_max_count = 0; + m_max_count = 0; + m_table_size = (size <= 0) ? MaxTableSize : size; + m_next_linear_id = MinLinearId; + m_count = 0; + m_free_head_index = -1; /* Free all entries. */ - for (size_t i = 0; i < static_cast(m_table_size - 1); i++) { - m_entries[i].SetFree(std::addressof(m_entries[i + 1])); + for (s32 i = 0; i < static_cast(m_table_size); ++i) { + m_objects[i] = nullptr; + m_entry_infos[i].next_free_index = i - 1; + m_free_head_index = i; } - m_entries[m_table_size - 1].SetFree(nullptr); - - m_free_head = std::addressof(m_entries[0]); return ResultSuccess(); } @@ -134,7 +116,7 @@ namespace ams::kern { if constexpr (std::is_same::value) { return this->GetObjectImpl(handle); } else { - if (auto *obj = this->GetObjectImpl(handle); obj != nullptr) { + if (auto *obj = this->GetObjectImpl(handle); AMS_LIKELY(obj != nullptr)) { return obj->DynamicCast(); } else { return nullptr; @@ -149,11 +131,15 @@ namespace ams::kern { /* Handle pseudo-handles. */ if constexpr (std::derived_from) { if (handle == ams::svc::PseudoHandle::CurrentProcess) { - return GetCurrentProcessPointer(); + auto * const cur_process = GetCurrentProcessPointer(); + AMS_ASSUME(cur_process != nullptr); + return cur_process; } } else if constexpr (std::derived_from) { if (handle == ams::svc::PseudoHandle::CurrentThread) { - return GetCurrentThreadPointer(); + auto * const cur_thread = GetCurrentThreadPointer(); + AMS_ASSUME(cur_thread != nullptr); + return cur_thread; } } @@ -177,8 +163,11 @@ namespace ams::kern { ALWAYS_INLINE KScopedAutoObject GetObjectForIpc(ams::svc::Handle handle, KThread *cur_thread) const { /* Handle pseudo-handles. */ + AMS_ASSUME(cur_thread != nullptr); if (handle == ams::svc::PseudoHandle::CurrentProcess) { - return static_cast(static_cast(cur_thread->GetOwnerProcess())); + auto * const cur_process = static_cast(static_cast(cur_thread->GetOwnerProcess())); + AMS_ASSUME(cur_process != nullptr); + return cur_process; } if (handle == ams::svc::PseudoHandle::CurrentThread) { return static_cast(cur_thread); @@ -256,27 +245,29 @@ namespace ams::kern { NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type); NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type); - constexpr ALWAYS_INLINE Entry *AllocateEntry() { + constexpr ALWAYS_INLINE s32 AllocateEntry() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(m_count < m_table_size); - Entry *entry = m_free_head; - m_free_head = entry->GetNextFreeEntry(); + const auto index = m_free_head_index; - m_count++; - m_max_count = std::max(m_max_count, m_count); + m_free_head_index = m_entry_infos[index].GetNextFreeIndex(); - return entry; + m_max_count = std::max(m_max_count, ++m_count); + + return index; } - constexpr ALWAYS_INLINE void FreeEntry(Entry *entry) { + constexpr ALWAYS_INLINE void FreeEntry(s32 index) { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(m_count > 0); - entry->SetFree(m_free_head); - m_free_head = entry; + m_objects[index] = nullptr; + m_entry_infos[index].next_free_index = m_free_head_index; - m_count--; + m_free_head_index = index; + + --m_count; } constexpr ALWAYS_INLINE u16 AllocateLinearId() { @@ -287,13 +278,7 @@ namespace ams::kern { return id; } - constexpr ALWAYS_INLINE size_t GetEntryIndex(Entry *entry) { - const size_t index = entry - m_table; - MESOSPHERE_ASSERT(index < m_table_size); - return index; - } - - constexpr ALWAYS_INLINE Entry *FindEntry(ams::svc::Handle handle) const { + constexpr ALWAYS_INLINE bool IsValidHandle(ams::svc::Handle handle) const { MESOSPHERE_ASSERT_THIS(); /* Unpack the handle. */ @@ -306,38 +291,38 @@ namespace ams::kern { MESOSPHERE_UNUSED(reserved); /* Validate our indexing information. */ - if (raw_value == 0) { - return nullptr; + if (AMS_UNLIKELY(raw_value == 0)) { + return false; } - if (linear_id == 0) { - return nullptr; + if (AMS_UNLIKELY(linear_id == 0)) { + return false; } - if (index >= m_table_size) { - return nullptr; + if (AMS_UNLIKELY(index >= m_table_size)) { + return false; } - /* Get the entry, and ensure our serial id is correct. */ - Entry *entry = std::addressof(m_table[index]); - if (entry->GetObject() == nullptr) { - return nullptr; + /* Check that there's an object, and our serial id is correct. */ + if (AMS_UNLIKELY(m_objects[index] == nullptr)) { + return false; } - if (entry->GetLinearId() != linear_id) { - return nullptr; + if (AMS_UNLIKELY(m_entry_infos[index].GetLinearId() != linear_id)) { + return false; } - return entry; + return true; } - constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const { + constexpr NOINLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const { MESOSPHERE_ASSERT_THIS(); /* Handles must not have reserved bits set. */ - if (GetHandleBitPack(handle).Get() != 0) { + const auto handle_pack = GetHandleBitPack(handle); + if (AMS_UNLIKELY(handle_pack.Get() != 0)) { return nullptr; } - if (Entry *entry = this->FindEntry(handle); entry != nullptr) { - return entry->GetObject(); + if (AMS_LIKELY(this->IsValidHandle(handle))) { + return m_objects[handle_pack.Get()]; } else { return nullptr; } @@ -347,18 +332,17 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Index must be in bounds. */ - if (index >= m_table_size || m_table == nullptr) { + if (AMS_UNLIKELY(index >= m_table_size)) { return nullptr; } /* Ensure entry has an object. */ - Entry *entry = std::addressof(m_table[index]); - if (entry->GetObject() == nullptr) { + if (KAutoObject *obj = m_objects[index]; obj != nullptr) { + *out_handle = EncodeHandle(index, m_entry_infos[index].GetLinearId()); + return obj; + } else { return nullptr; } - - *out_handle = EncodeHandle(index, entry->GetLinearId()); - return entry->GetObject(); } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp index db3d58e6b..97b7977f9 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp @@ -70,6 +70,7 @@ namespace ams::kern { constexpr bool Is64Bit() const { return (m_flags & (1 << 3)); } constexpr bool Is64BitAddressSpace() const { return (m_flags & (1 << 4)); } constexpr bool UsesSecureMemory() const { return (m_flags & (1 << 5)); } + constexpr bool IsImmortal() const { return (m_flags & (1 << 6)); } constexpr u32 GetRxAddress() const { return m_rx_address; } constexpr u32 GetRxSize() const { return m_rx_size; } @@ -90,45 +91,49 @@ namespace ams::kern { class KInitialProcessReader { private: - KInitialProcessHeader *m_kip_header; + KInitialProcessHeader m_kip_header; public: constexpr KInitialProcessReader() : m_kip_header() { /* ... */ } - constexpr const u32 *GetCapabilities() const { return m_kip_header->GetCapabilities(); } - constexpr size_t GetNumCapabilities() const { return m_kip_header->GetNumCapabilities(); } + constexpr const u32 *GetCapabilities() const { return m_kip_header.GetCapabilities(); } + constexpr size_t GetNumCapabilities() const { return m_kip_header.GetNumCapabilities(); } constexpr size_t GetBinarySize() const { - return sizeof(*m_kip_header) + m_kip_header->GetRxCompressedSize() + m_kip_header->GetRoCompressedSize() + m_kip_header->GetRwCompressedSize(); + return m_kip_header.GetRxCompressedSize() + m_kip_header.GetRoCompressedSize() + m_kip_header.GetRwCompressedSize(); } constexpr size_t GetSize() const { - if (const size_t bss_size = m_kip_header->GetBssSize(); bss_size != 0) { - return m_kip_header->GetBssAddress() + m_kip_header->GetBssSize(); + if (const size_t bss_size = m_kip_header.GetBssSize(); bss_size != 0) { + return util::AlignUp(m_kip_header.GetBssAddress() + m_kip_header.GetBssSize(), PageSize); } else { - return m_kip_header->GetRwAddress() + m_kip_header->GetRwSize(); + return util::AlignUp(m_kip_header.GetRwAddress() + m_kip_header.GetRwSize(), PageSize); } } - constexpr u8 GetPriority() const { return m_kip_header->GetPriority(); } - constexpr u8 GetIdealCoreId() const { return m_kip_header->GetIdealCoreId(); } - constexpr u32 GetAffinityMask() const { return m_kip_header->GetAffinityMask(); } - constexpr u32 GetStackSize() const { return m_kip_header->GetStackSize(); } + constexpr u8 GetPriority() const { return m_kip_header.GetPriority(); } + constexpr u8 GetIdealCoreId() const { return m_kip_header.GetIdealCoreId(); } + constexpr u32 GetAffinityMask() const { return m_kip_header.GetAffinityMask(); } + constexpr u32 GetStackSize() const { return m_kip_header.GetStackSize(); } - constexpr bool Is64Bit() const { return m_kip_header->Is64Bit(); } - constexpr bool Is64BitAddressSpace() const { return m_kip_header->Is64BitAddressSpace(); } - constexpr bool UsesSecureMemory() const { return m_kip_header->UsesSecureMemory(); } + constexpr bool Is64Bit() const { return m_kip_header.Is64Bit(); } + constexpr bool Is64BitAddressSpace() const { return m_kip_header.Is64BitAddressSpace(); } + constexpr bool UsesSecureMemory() const { return m_kip_header.UsesSecureMemory(); } + constexpr bool IsImmortal() const { return m_kip_header.IsImmortal(); } - bool Attach(u8 *bin) { - if (KInitialProcessHeader *header = reinterpret_cast(bin); header->IsValid()) { - m_kip_header = header; - return true; + KVirtualAddress Attach(KVirtualAddress bin) { + /* Copy the header. */ + m_kip_header = *GetPointer(bin); + + /* Check that it's valid. */ + if (m_kip_header.IsValid()) { + return bin + sizeof(KInitialProcessHeader); } else { - return false; + return Null; } } Result MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const; - Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms) const; + Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms, KProcessAddress src) const; Result SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter ¶ms) const; }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp index acc72dc0d..08808ab94 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp @@ -28,9 +28,10 @@ namespace ams::kern { MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent); private: s32 m_interrupt_id; + s32 m_core_id; bool m_is_initialized; public: - constexpr KInterruptEvent() : m_interrupt_id(-1), m_is_initialized(false) { /* ... */ } + constexpr KInterruptEvent() : m_interrupt_id(-1), m_core_id(-1), m_is_initialized(false) { /* ... */ } virtual ~KInterruptEvent() { /* ... */ } Result Initialize(int32_t interrupt_name, ams::svc::InterruptType type); @@ -58,9 +59,9 @@ namespace ams::kern { virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override; virtual void DoTask() override; - void Unregister(s32 interrupt_id); + void Unregister(s32 interrupt_id, s32 core_id); public: - static Result Register(s32 interrupt_id, bool level, KInterruptEvent *event); + static Result Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp index 5fdc7ac85..425714815 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp @@ -24,40 +24,59 @@ namespace ams::kern { class KLightConditionVariable { private: - KThreadQueue m_thread_queue; + KThread::WaiterList m_wait_list; public: - constexpr ALWAYS_INLINE KLightConditionVariable() : m_thread_queue() { /* ... */ } + constexpr ALWAYS_INLINE KLightConditionVariable() : m_wait_list() { /* ... */ } private: - void WaitImpl(KLightLock *lock, s64 timeout) { + void WaitImpl(KLightLock *lock, s64 timeout, bool allow_terminating_thread) { KThread *owner = GetCurrentThreadPointer(); KHardwareTimer *timer; /* Sleep the thread. */ { KScopedSchedulerLockAndSleep lk(&timer, owner, timeout); - lock->Unlock(); - if (!m_thread_queue.SleepThread(owner)) { + if (!allow_terminating_thread && owner->IsTerminationRequested()) { lk.CancelSleep(); return; } + + lock->Unlock(); + + + /* Set the thread as waiting. */ + GetCurrentThread().SetState(KThread::ThreadState_Waiting); + + /* Add the thread to the queue. */ + m_wait_list.push_back(GetCurrentThread()); + } + + /* Remove the thread from the wait list. */ + { + KScopedSchedulerLock sl; + + m_wait_list.erase(m_wait_list.iterator_to(GetCurrentThread())); } /* Cancel the task that the sleep setup. */ if (timer != nullptr) { timer->CancelTask(owner); } + + /* Re-acquire the lock. */ + lock->Lock(); } public: - void Wait(KLightLock *lock, s64 timeout = -1ll) { - this->WaitImpl(lock, timeout); - lock->Lock(); + void Wait(KLightLock *lock, s64 timeout = -1ll, bool allow_terminating_thread = true) { + this->WaitImpl(lock, timeout, allow_terminating_thread); } void Broadcast() { KScopedSchedulerLock lk; - while (m_thread_queue.WakeupFrontThread() != nullptr) { - /* We want to signal all threads, and so should continue waking up until there's nothing to wake. */ + + /* Signal all threads. */ + for (auto &thread : m_wait_list) { + thread.SetState(KThread::ThreadState_Runnable); } } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp deleted file mode 100644 index 2ed4cb900..000000000 --- a/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include -#include - -namespace ams::kern { - - class KLinkedListNode : public util::IntrusiveListBaseNode, public KSlabAllocated { - private: - void *m_item; - public: - constexpr KLinkedListNode() : util::IntrusiveListBaseNode(), m_item(nullptr) { MESOSPHERE_ASSERT_THIS(); } - - constexpr void Initialize(void *it) { - MESOSPHERE_ASSERT_THIS(); - m_item = it; - } - - constexpr void *GetItem() const { - return m_item; - } - }; - static_assert(sizeof(KLinkedListNode) == sizeof(util::IntrusiveListNode) + sizeof(void *)); - - template - class KLinkedList : private util::IntrusiveListBaseTraits::ListType { - private: - using BaseList = util::IntrusiveListBaseTraits::ListType; - public: - template - class Iterator; - - using value_type = T; - using size_type = size_t; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using const_pointer = const value_type *; - using reference = value_type &; - using const_reference = const value_type &; - using iterator = Iterator; - using const_iterator = Iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - template - class Iterator { - private: - using BaseIterator = BaseList::Iterator; - friend class KLinkedList; - public: - using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename KLinkedList::value_type; - using difference_type = typename KLinkedList::difference_type; - using pointer = typename std::conditional::type; - using reference = typename std::conditional::type; - private: - BaseIterator m_base_it; - public: - explicit Iterator(BaseIterator it) : m_base_it(it) { /* ... */ } - - pointer GetItem() const { - return static_cast(m_base_it->GetItem()); - } - - bool operator==(const Iterator &rhs) const { - return m_base_it == rhs.m_base_it; - } - - bool operator!=(const Iterator &rhs) const { - return !(*this == rhs); - } - - pointer operator->() const { - return this->GetItem(); - } - - reference operator*() const { - return *this->GetItem(); - } - - Iterator &operator++() { - ++m_base_it; - return *this; - } - - Iterator &operator--() { - --m_base_it; - return *this; - } - - Iterator operator++(int) { - const Iterator it{*this}; - ++(*this); - return it; - } - - Iterator operator--(int) { - const Iterator it{*this}; - --(*this); - return it; - } - - operator Iterator() const { - return Iterator(m_base_it); - } - }; - public: - constexpr KLinkedList() : BaseList() { /* ... */ } - - ~KLinkedList() { - /* Erase all elements. */ - for (auto it = this->begin(); it != this->end(); it = this->erase(it)) { - /* ... */ - } - - /* Ensure we succeeded. */ - MESOSPHERE_ASSERT(this->empty()); - } - - /* Iterator accessors. */ - iterator begin() { - return iterator(BaseList::begin()); - } - - const_iterator begin() const { - return const_iterator(BaseList::begin()); - } - - iterator end() { - return iterator(BaseList::end()); - } - - const_iterator end() const { - return const_iterator(BaseList::end()); - } - - const_iterator cbegin() const { - return this->begin(); - } - - const_iterator cend() const { - return this->end(); - } - - reverse_iterator rbegin() { - return reverse_iterator(this->end()); - } - - const_reverse_iterator rbegin() const { - return const_reverse_iterator(this->end()); - } - - reverse_iterator rend() { - return reverse_iterator(this->begin()); - } - - const_reverse_iterator rend() const { - return const_reverse_iterator(this->begin()); - } - - const_reverse_iterator crbegin() const { - return this->rbegin(); - } - - const_reverse_iterator crend() const { - return this->rend(); - } - - /* Content management. */ - using BaseList::empty; - using BaseList::size; - - reference back() { - return *(--this->end()); - } - - const_reference back() const { - return *(--this->end()); - } - - reference front() { - return *this->begin(); - } - - const_reference front() const { - return *this->begin(); - } - - iterator insert(const_iterator pos, reference ref) { - KLinkedListNode *node = KLinkedListNode::Allocate(); - MESOSPHERE_ABORT_UNLESS(node != nullptr); - node->Initialize(std::addressof(ref)); - return iterator(BaseList::insert(pos.m_base_it, *node)); - } - - void push_back(reference ref) { - this->insert(this->end(), ref); - } - - void push_front(reference ref) { - this->insert(this->begin(), ref); - } - - void pop_back() { - this->erase(--this->end()); - } - - void pop_front() { - this->erase(this->begin()); - } - - iterator erase(const iterator pos) { - KLinkedListNode *freed_node = std::addressof(*pos.m_base_it); - iterator ret = iterator(BaseList::erase(pos.m_base_it)); - KLinkedListNode::Free(freed_node); - - return ret; - } - }; - -} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp index a262e99b6..b9b96f5a4 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp @@ -160,7 +160,7 @@ namespace ams::kern { }; constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) { - return static_cast((perm & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((perm & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None)); + return static_cast((util::ToUnderlying(perm) & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((util::ToUnderlying(perm) & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None)); } enum KMemoryAttribute : u8 { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp index 605711891..e25a0dcc2 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp @@ -27,18 +27,8 @@ namespace ams::kern { KMemoryBlock *m_blocks[MaxBlocks]; size_t m_index; KMemoryBlockSlabManager *m_slab_manager; - public: - constexpr explicit KMemoryBlockManagerUpdateAllocator(KMemoryBlockSlabManager *sm) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { /* ... */ } - - ~KMemoryBlockManagerUpdateAllocator() { - for (const auto &block : m_blocks) { - if (block != nullptr) { - m_slab_manager->Free(block); - } - } - } - - Result Initialize(size_t num_blocks) { + private: + ALWAYS_INLINE Result Initialize(size_t num_blocks) { /* Check num blocks. */ MESOSPHERE_ASSERT(num_blocks <= MaxBlocks); @@ -53,6 +43,18 @@ namespace ams::kern { return ResultSuccess(); } + public: + KMemoryBlockManagerUpdateAllocator(Result *out_result, KMemoryBlockSlabManager *sm, size_t num_blocks = MaxBlocks) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { + *out_result = this->Initialize(num_blocks); + } + + ~KMemoryBlockManagerUpdateAllocator() { + for (const auto &block : m_blocks) { + if (block != nullptr) { + m_slab_manager->Free(block); + } + } + } KMemoryBlock *Allocate() { MESOSPHERE_ABORT_UNLESS(m_index < MaxBlocks); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 574c48749..e38a361dc 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -44,9 +44,8 @@ namespace ams::kern { constexpr size_t KernelInitialPageHeapSize = 128_KB; constexpr size_t KernelSlabHeapDataSize = 5_MB; - constexpr size_t KernelSlabHeapGapsSize = 2_MB - 64_KB; - constexpr size_t KernelSlabHeapGapsSizeDeprecated = 2_MB; - constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; + constexpr size_t KernelSlabHeapGapsSizeMax = 2_MB - 64_KB; + constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; /* NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. */ constexpr size_t KernelSlabHeapAdditionalSize = 0x68000; @@ -176,7 +175,14 @@ namespace ams::kern { return std::make_tuple(total_size, kernel_size); } - static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start); + static void InitializeLinearMemoryAddresses(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) { + /* Set static differences. */ + s_linear_phys_to_virt_diff = GetInteger(linear_virtual_start) - GetInteger(aligned_linear_phys_start); + s_linear_virt_to_phys_diff = GetInteger(aligned_linear_phys_start) - GetInteger(linear_virtual_start); + } + + static void InitializeLinearMemoryRegionTrees(); + static size_t GetResourceRegionSizeForInit(); static NOINLINE auto GetKernelRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp index 5bef180a9..4b17911e4 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp @@ -75,7 +75,7 @@ namespace ams::kern { KVirtualAddress AllocateBlock(s32 index, bool random) { return m_heap.AllocateBlock(index, random); } void Free(KVirtualAddress addr, size_t num_pages) { m_heap.Free(addr, num_pages); } - void UpdateUsedHeapSize() { m_heap.UpdateUsedSize(); } + void SetInitialUsedHeapSize(size_t reserved_size) { m_heap.SetInitialUsedSize(reserved_size); } void InitializeOptimizedMemory() { std::memset(GetVoidPointer(m_management_region), 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); } @@ -168,6 +168,10 @@ namespace ams::kern { return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()]; } + const Impl &GetManager(KVirtualAddress address) const { + return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()]; + } + constexpr Impl *GetFirstManager(Pool pool, Direction dir) { return dir == Direction_FromBack ? m_pool_managers_tail[pool] : m_pool_managers_head[pool]; } @@ -197,6 +201,10 @@ namespace ams::kern { NOINLINE Result AllocateAndOpen(KPageGroup *out, size_t num_pages, u32 option); NOINLINE Result AllocateAndOpenForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern); + Pool GetPool(KVirtualAddress address) const { + return this->GetManager(address).GetPool(); + } + void Open(KVirtualAddress address, size_t num_pages) { /* Repeatedly open references until we've done so for all pages. */ while (num_pages) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp index 5c77ed363..5ffe4ca05 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp @@ -21,7 +21,8 @@ namespace ams::kern { enum KMemoryRegionType : u32 {}; enum KMemoryRegionAttr : typename std::underlying_type::type { - KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_CarveoutProtected = 0x02000000, + KMemoryRegionAttr_Uncached = 0x04000000, KMemoryRegionAttr_DidKernelMap = 0x08000000, KMemoryRegionAttr_ShouldKernelMap = 0x10000000, KMemoryRegionAttr_UserReadOnly = 0x20000000, @@ -65,11 +66,41 @@ namespace ams::kern { consteval operator KMemoryRegionType() const { return static_cast(m_value); } consteval ValueType GetValue() const { return m_value; } - consteval const KMemoryRegionTypeValue &Finalize() { m_finalized = true; return *this; } - consteval const KMemoryRegionTypeValue &SetSparseOnly() { m_sparse_only = true; return *this; } - consteval const KMemoryRegionTypeValue &SetDenseOnly() { m_dense_only = true; return *this; } + consteval const KMemoryRegionTypeValue Finalize() { + AMS_ASSUME(!m_finalized); - consteval KMemoryRegionTypeValue &SetAttribute(KMemoryRegionAttr attr) { AMS_ASSUME(!m_finalized); m_value |= attr; return *this; } + KMemoryRegionTypeValue new_type = *this; + new_type.m_finalized = true; + return new_type; + } + + consteval const KMemoryRegionTypeValue SetSparseOnly() { + AMS_ASSUME(!m_finalized); + AMS_ASSUME(!m_sparse_only); + AMS_ASSUME(!m_dense_only); + + KMemoryRegionTypeValue new_type = *this; + new_type.m_sparse_only = true; + return new_type; + } + + consteval const KMemoryRegionTypeValue SetDenseOnly() { + AMS_ASSUME(!m_finalized); + AMS_ASSUME(!m_sparse_only); + AMS_ASSUME(!m_dense_only); + + KMemoryRegionTypeValue new_type = *this; + new_type.m_dense_only = true; + return new_type; + } + + consteval KMemoryRegionTypeValue SetAttribute(KMemoryRegionAttr attr) { + AMS_ASSUME(!m_finalized); + + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= attr; + return new_type; + } consteval KMemoryRegionTypeValue DeriveInitial(size_t i, size_t next = BITSIZEOF(ValueType)) const { AMS_ASSUME(!m_finalized); @@ -216,6 +247,10 @@ namespace ams::kern { static_assert(KMemoryRegionType_VirtualDramKernelPtHeap .GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); + /* UNUSED: .DeriveSparse(2, 2, 0); */ + constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug = KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); + static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); + constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); constexpr inline const auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); @@ -292,6 +327,8 @@ namespace ams::kern { return KMemoryRegionType_VirtualDramKernelTraceBuffer; } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { return KMemoryRegionType_VirtualDramKernelPtHeap; + } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { + return KMemoryRegionType_VirtualDramUnknownDebug; } else { return KMemoryRegionType_Dram; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp index f97a16d4b..f0bf6b8a0 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp @@ -47,6 +47,9 @@ namespace ams::kern { Derived *derived = obj->DynamicCast(); R_UNLESS(derived != nullptr, svc::ResultNotFound()); + /* Check that the object is closed. */ + R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState()); + return Delete(obj.GetPointerUnsafe(), name); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp index e14e7e0df..563bcb3be 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp @@ -125,7 +125,7 @@ namespace ams::kern { private: KVirtualAddress m_heap_address; size_t m_heap_size; - size_t m_used_size; + size_t m_initial_used_size; size_t m_num_blocks; Block m_blocks[NumMemoryBlockPageShifts]; private: @@ -134,7 +134,7 @@ namespace ams::kern { void FreeBlock(KVirtualAddress block, s32 index); public: - KPageHeap() : m_heap_address(), m_heap_size(), m_used_size(), m_num_blocks(), m_blocks() { /* ... */ } + KPageHeap() : m_heap_address(), m_heap_size(), m_initial_used_size(), m_num_blocks(), m_blocks() { /* ... */ } constexpr KVirtualAddress GetAddress() const { return m_heap_address; } constexpr size_t GetSize() const { return m_heap_size; } @@ -149,8 +149,13 @@ namespace ams::kern { size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; } void DumpFreeList() const; - void UpdateUsedSize() { - m_used_size = m_heap_size - (this->GetNumFreePages() * PageSize); + void SetInitialUsedSize(size_t reserved_size) { + /* Check that the reserved size is valid. */ + const size_t free_size = this->GetNumFreePages() * PageSize; + MESOSPHERE_ABORT_UNLESS(m_heap_size >= free_size + reserved_size); + + /* Set the initial used size. */ + m_initial_used_size = m_heap_size - free_size - reserved_size; } KVirtualAddress AllocateBlock(s32 index, bool random); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 2eafc4c49..4a9116f1f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -47,12 +47,21 @@ namespace ams::kern { static_assert(std::is_trivial::value); static_assert(sizeof(KPageProperties) == sizeof(u32)); + class KResourceLimit; + class KPageTableBase { NON_COPYABLE(KPageTableBase); NON_MOVEABLE(KPageTableBase); public: using TraversalEntry = KPageTableImpl::TraversalEntry; using TraversalContext = KPageTableImpl::TraversalContext; + + struct MemoryRange { + KVirtualAddress address; + size_t size; + + void Close(); + }; protected: enum MemoryFillValue { MemoryFillValue_Zero = 0, @@ -98,8 +107,11 @@ namespace ams::kern { Node *Peek() const { return m_root; } Node *Pop() { - Node *r = m_root; - m_root = m_root->m_next; + Node * const r = m_root; + + m_root = r->m_next; + r->m_next = nullptr; + return r; } }; @@ -150,8 +162,10 @@ namespace ams::kern { size_t m_max_heap_size{}; size_t m_mapped_physical_memory_size{}; size_t m_mapped_unsafe_physical_memory{}; + size_t m_mapped_ipc_server_memory{}; mutable KLightLock m_general_lock{}; mutable KLightLock m_map_physical_memory_lock{}; + KLightLock m_device_map_lock{}; KPageTableImpl m_impl{}; KMemoryBlockManager m_memory_block_manager{}; u32 m_allocate_option{}; @@ -161,6 +175,7 @@ namespace ams::kern { bool m_enable_device_address_space_merge{}; KMemoryBlockSlabManager *m_memory_block_slab_manager{}; KBlockInfoManager *m_block_info_manager{}; + KResourceLimit *m_resource_limit{}; const KMemoryRegion *m_cached_physical_linear_region{}; const KMemoryRegion *m_cached_physical_heap_region{}; const KMemoryRegion *m_cached_virtual_heap_region{}; @@ -171,7 +186,7 @@ namespace ams::kern { constexpr KPageTableBase() { /* ... */ } NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager); + NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit); void Finalize(); @@ -195,6 +210,10 @@ namespace ams::kern { return this->CanContain(addr, size, KMemoryState_AliasCode); } + ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() { + return KScopedLightLock(m_device_map_lock); + } + KProcessAddress GetRegionAddress(KMemoryState state) const; size_t GetRegionSize(KMemoryState state) const; bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const; @@ -286,16 +305,35 @@ namespace ams::kern { Result MakePageGroup(KPageGroup &pg, KProcessAddress addr, size_t num_pages); bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages); + Result GetContiguousMemoryRangeWithState(MemoryRange *out, KProcessAddress address, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); + NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); + Result MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); + Result ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size); + Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size); + Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state); Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send); void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm); size_t GetSize(KMemoryState state) const; + + ALWAYS_INLINE bool GetPhysicalAddressLocked(KPhysicalAddress *out, KProcessAddress virt_addr) const { + /* Validate pre-conditions. */ + MESOSPHERE_AUDIT(this->IsLockedByCurrentThread()); + + return this->GetImpl().GetPhysicalAddress(out, virt_addr); + } public: bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const { - return this->GetImpl().GetPhysicalAddress(out, virt_addr); + /* Validate pre-conditions. */ + MESOSPHERE_AUDIT(!this->IsLockedByCurrentThread()); + + /* Acquire exclusive access to the table while doing address translation. */ + KScopedLightLock lk(m_general_lock); + + return this->GetPhysicalAddressLocked(out, virt_addr); } KBlockInfoManager *GetBlockInfoManager() const { return m_block_info_manager; } @@ -341,14 +379,20 @@ namespace ams::kern { Result InvalidateProcessDataCache(KProcessAddress address, size_t size); Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size); + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size); + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size); + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size); + + Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); + Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size); - Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); - - Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size); Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size); + Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); + Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange *out, KProcessAddress address, size_t size); + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size); Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); @@ -357,6 +401,8 @@ namespace ams::kern { Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size); Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg); + Result OpenMemoryRangeForProcessCacheOperation(MemoryRange *out, KProcessAddress address, size_t size); + Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result CopyMemoryFromLinearToKernel(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr); @@ -365,7 +411,7 @@ namespace ams::kern { Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send); - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); Result MapPhysicalMemory(KProcessAddress address, size_t size); @@ -374,6 +420,8 @@ namespace ams::kern { Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); + Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase &src_pt, KProcessAddress src_address); + void DumpMemoryBlocksLocked() const { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); m_memory_block_manager.DumpBlocks(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_manager.hpp index ca8edf5b5..fc5652ed1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_manager.hpp @@ -25,18 +25,20 @@ namespace ams::kern { class PageTablePage { private: u8 m_buffer[PageSize]; + public: + ALWAYS_INLINE PageTablePage() { /* Do not initialize anything. */ } }; static_assert(sizeof(PageTablePage) == PageSize); } - class KPageTableManager : public KDynamicSlabHeap { + class KPageTableManager : public KDynamicSlabHeap { public: using RefCount = u16; static constexpr size_t PageTableSize = sizeof(impl::PageTablePage); static_assert(PageTableSize == PageSize); private: - using BaseHeap = KDynamicSlabHeap; + using BaseHeap = KDynamicSlabHeap; private: RefCount *m_ref_counts; public: @@ -72,9 +74,6 @@ namespace ams::kern { } void Free(KVirtualAddress addr) { - /* Ensure all pages in the heap are zero. */ - cpu::ClearPageToZero(GetVoidPointer(addr)); - /* Free the page. */ BaseHeap::Free(GetPointer(addr)); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp index 6182ab537..2929fad62 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp @@ -53,6 +53,11 @@ namespace ams::kern { uintptr_t GetName() const { return m_name; } bool IsLight() const { return m_is_light; } + bool IsServerClosed() const { + KScopedSchedulerLock sl; + return m_state == State::ServerClosed; + } + Result EnqueueSession(KServerSession *session); Result EnqueueSession(KLightServerSession *session); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 9e5dd82f7..af3e59c7b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -77,8 +77,7 @@ namespace ams::kern { bool m_is_initialized{}; bool m_is_application{}; char m_name[13]{}; - std::atomic m_num_threads{}; - u16 m_peak_num_threads{}; + std::atomic m_num_running_threads{}; u32 m_flags{}; KMemoryManager::Pool m_memory_pool{}; s64 m_schedule_count{}; @@ -99,7 +98,9 @@ namespace ams::kern { SharedMemoryInfoList m_shared_memory_list{}; BetaList m_beta_list{}; bool m_is_suspended{}; + bool m_is_immortal{}; bool m_is_jit_debug{}; + bool m_is_handle_table_initialized{}; ams::svc::DebugEvent m_jit_debug_event_type{}; ams::svc::DebugException m_jit_debug_exception_type{}; uintptr_t m_jit_debug_params[4]{}; @@ -108,7 +109,6 @@ namespace ams::kern { KThread *m_running_threads[cpu::NumCores]{}; u64 m_running_thread_idle_counts[cpu::NumCores]{}; KThread *m_pinned_threads[cpu::NumCores]{}; - std::atomic m_num_created_threads{}; std::atomic m_cpu_time{}; std::atomic m_num_process_switches{}; std::atomic m_num_thread_switches{}; @@ -124,17 +124,17 @@ namespace ams::kern { private: Result Initialize(const ams::svc::CreateProcessParameter ¶ms); - void StartTermination(); + Result StartTermination(); void FinishTermination(); - void PinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void PinThread(s32 core_id, KThread *thread) { MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); MESOSPHERE_ASSERT(m_pinned_threads[core_id] == nullptr); m_pinned_threads[core_id] = thread; } - void UnpinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void UnpinThread(s32 core_id, KThread *thread) { MESOSPHERE_UNUSED(thread); MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); @@ -145,7 +145,7 @@ namespace ams::kern { KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ } - Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); + Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal); Result Initialize(const ams::svc::CreateProcessParameter ¶ms, svc::KUserPointer caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); void Exit(); @@ -285,8 +285,8 @@ namespace ams::kern { constexpr s64 GetScheduledCount() const { return m_schedule_count; } void IncrementScheduledCount() { ++m_schedule_count; } - void IncrementThreadCount(); - void DecrementThreadCount(); + void IncrementRunningThreadCount(); + void DecrementRunningThreadCount(); size_t GetTotalSystemResourceSize() const { return m_system_resource_num_pages * PageSize; } size_t GetUsedSystemResourceSize() const { @@ -340,6 +340,7 @@ namespace ams::kern { void PinCurrentThread(); void UnpinCurrentThread(); + void UnpinThread(KThread *thread); Result SignalToAddress(KProcessAddress address) { return m_cond_var.SignalToAddress(address); @@ -405,6 +406,23 @@ namespace ams::kern { this->NotifyAvailable(); } } + + ALWAYS_INLINE Result InitializeHandleTable(s32 size) { + /* Try to initialize the handle table. */ + R_TRY(m_handle_table.Initialize(size)); + + /* We succeeded, so note that we did. */ + m_is_handle_table_initialized = true; + return ResultSuccess(); + } + + ALWAYS_INLINE void FinalizeHandleTable() { + /* Finalize the table. */ + m_handle_table.Finalize(); + + /* Note that the table is finalized. */ + m_is_handle_table_initialized = false; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index 5bcbab66f..942365e22 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -194,8 +194,20 @@ namespace ams::kern { static bool s_scheduler_update_needed; static KSchedulerPriorityQueue s_priority_queue; static LockType s_scheduler_lock; + public: + static consteval bool ValidateAssemblyOffsets(); }; + consteval bool KScheduler::ValidateAssemblyOffsets() { + static_assert(__builtin_offsetof(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING); + static_assert(__builtin_offsetof(KScheduler, m_state.interrupt_task_thread_runnable) == KSCHEDULER_INTERRUPT_TASK_THREAD_RUNNABLE); + static_assert(__builtin_offsetof(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD); + static_assert(__builtin_offsetof(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK); + + return true; + } + static_assert(KScheduler::ValidateAssemblyOffsets()); + class KScopedSchedulerLock : KScopedLock { public: explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp index c00b9dd63..2173c3964 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp @@ -37,14 +37,22 @@ namespace ams::kern { private: KServerSession m_server; KClientSession m_client; - State m_state; + std::atomic::type> m_atomic_state; KClientPort *m_port; uintptr_t m_name; KProcess *m_process; bool m_initialized; + private: + ALWAYS_INLINE void SetState(State state) { + m_atomic_state = static_cast(state); + } + + ALWAYS_INLINE State GetState() const { + return static_cast(m_atomic_state.load()); + } public: constexpr KSession() - : m_server(), m_client(), m_state(State::Invalid), m_port(), m_name(), m_process(), m_initialized() + : m_server(), m_client(), m_atomic_state(static_cast::type>(State::Invalid)), m_port(), m_name(), m_process(), m_initialized() { /* ... */ } @@ -62,8 +70,8 @@ namespace ams::kern { void OnServerClosed(); void OnClientClosed(); - bool IsServerClosed() const { return m_state != State::Normal; } - bool IsClientClosed() const { return m_state != State::Normal; } + bool IsServerClosed() const { return this->GetState() != State::Normal; } + bool IsClientClosed() const { return this->GetState() != State::Normal; } Result OnRequest(KSessionRequest *request) { return m_server.OnRequest(request); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp index d3eb06678..6afa5c94d 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp @@ -216,7 +216,7 @@ namespace ams::kern { T *Allocate() { T *obj = reinterpret_cast(this->AllocateImpl()); if (AMS_LIKELY(obj != nullptr)) { - new (obj) T(); + std::construct_at(obj); } return obj; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp index f1bf59197..1465bbe69 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp @@ -16,7 +16,6 @@ #pragma once #include #include -#include namespace ams::kern { @@ -46,7 +45,7 @@ namespace ams::kern { static Result Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout); public: virtual void Finalize() override; - virtual bool IsSignaled() const = 0; + virtual bool IsSignaled() const { AMS_INFINITE_LOOP(); } virtual void DumpWaiters(); }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 184d55f37..10bcb4d32 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -77,22 +77,34 @@ namespace ams::kern { }; enum DpcFlag : u32 { - DpcFlag_Terminating = (1 << 0), - DpcFlag_Terminated = (1 << 1), + DpcFlag_Terminating = (1 << 0), + DpcFlag_Terminated = (1 << 1), + DpcFlag_PerformDestruction = (1 << 2), }; struct StackParameters { - alignas(0x10) u8 svc_permission[0x10]; + alignas(0x10) u8 svc_permission[0x18]; + KThreadContext *context; + KThread *cur_thread; + s16 disable_count; std::atomic dpc_flags; u8 current_svc_id; bool is_calling_svc; bool is_in_exception_handler; bool is_pinned; - s32 disable_count; - KThreadContext *context; - KThread *cur_thread; }; static_assert(alignof(StackParameters) == 0x10); + static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE); + + static_assert(__builtin_offsetof(StackParameters, svc_permission) == THREAD_STACK_PARAMETERS_SVC_PERMISSION); + static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT); + static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD); + static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT); + static_assert(__builtin_offsetof(StackParameters, dpc_flags) == THREAD_STACK_PARAMETERS_DPC_FLAGS); + static_assert(__builtin_offsetof(StackParameters, current_svc_id) == THREAD_STACK_PARAMETERS_CURRENT_SVC_ID); + static_assert(__builtin_offsetof(StackParameters, is_calling_svc) == THREAD_STACK_PARAMETERS_IS_CALLING_SVC); + static_assert(__builtin_offsetof(StackParameters, is_in_exception_handler) == THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER); + static_assert(__builtin_offsetof(StackParameters, is_pinned) == THREAD_STACK_PARAMETERS_IS_PINNED); struct QueueEntry { private: @@ -124,7 +136,7 @@ namespace ams::kern { static_assert(sizeof(SyncObjectBuffer::m_sync_objects) == sizeof(SyncObjectBuffer::m_handles)); struct ConditionVariableComparator { - struct LightCompareType { + struct RedBlackKeyType { uintptr_t m_cv_key; s32 m_priority; @@ -137,7 +149,7 @@ namespace ams::kern { } }; - template requires (std::same_as || std::same_as) + template requires (std::same_as || std::same_as) static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KThread &rhs) { const uintptr_t l_key = lhs.GetConditionVariableKey(); const uintptr_t r_key = rhs.GetConditionVariableKey(); @@ -153,8 +165,8 @@ namespace ams::kern { } } }; - static_assert(ams::util::HasLightCompareType); - static_assert(std::same_as, ConditionVariableComparator::LightCompareType>); + static_assert(ams::util::HasRedBlackKeyType); + static_assert(std::same_as, ConditionVariableComparator::RedBlackKeyType>); private: static inline std::atomic s_next_thread_id = 0; private: @@ -192,12 +204,14 @@ namespace ams::kern { WaiterList m_pinned_waiter_list{}; KThread *m_lock_owner{}; uintptr_t m_debug_params[3]{}; + KAutoObject *m_closed_object{}; u32 m_address_key_value{}; u32 m_suspend_request_flags{}; u32 m_suspend_allowed_flags{}; Result m_wait_result; Result m_debug_exception_result; s32 m_base_priority{}; + s32 m_base_priority_on_unpin{}; s32 m_physical_ideal_core_id{}; s32 m_virtual_ideal_core_id{}; s32 m_num_kernel_waiters{}; @@ -251,7 +265,7 @@ namespace ams::kern { return *(reinterpret_cast(m_kernel_stack_top) - 1); } public: - ALWAYS_INLINE s32 GetDisableDispatchCount() const { + ALWAYS_INLINE s16 GetDisableDispatchCount() const { MESOSPHERE_ASSERT_THIS(); return this->GetStackParameters().disable_count; } @@ -312,15 +326,15 @@ namespace ams::kern { } ALWAYS_INLINE void RegisterDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags |= flag; + this->GetStackParameters().dpc_flags.fetch_or(flag); } ALWAYS_INLINE void ClearDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags &= ~flag; + this->GetStackParameters().dpc_flags.fetch_and(~flag); } ALWAYS_INLINE u8 GetDpc() const { - return this->GetStackParameters().dpc_flags; + return this->GetStackParameters().dpc_flags.load(); } ALWAYS_INLINE bool HasDpc() const { @@ -328,13 +342,15 @@ namespace ams::kern { return this->GetDpc() != 0; } private: - void Suspend(); + void UpdateState(); ALWAYS_INLINE void AddWaiterImpl(KThread *thread); ALWAYS_INLINE void RemoveWaiterImpl(KThread *thread); ALWAYS_INLINE static void RestorePriority(KThread *thread); void StartTermination(); void FinishTermination(); + + void IncreaseBasePriority(s32 priority); public: constexpr u64 GetThreadId() const { return m_thread_id; } @@ -471,12 +487,45 @@ namespace ams::kern { constexpr void *GetThreadLocalRegionHeapAddress() const { return m_tls_heap_address; } constexpr KSynchronizationObject **GetSynchronizationObjectBuffer() { return std::addressof(m_sync_object_buffer.m_sync_objects[0]); } - constexpr ams::svc::Handle *GetHandleBuffer() { return std::addressof(m_sync_object_buffer.m_handles[sizeof(m_sync_object_buffer.m_sync_objects) / sizeof(ams::svc::Handle) - ams::svc::ArgumentHandleCountMax]); } + constexpr ams::svc::Handle *GetHandleBuffer() { return std::addressof(m_sync_object_buffer.m_handles[sizeof(m_sync_object_buffer.m_sync_objects) / (sizeof(ams::svc::Handle)) - ams::svc::ArgumentHandleCountMax]); } u16 GetUserDisableCount() const { return static_cast(m_tls_heap_address)->disable_count; } void SetInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 1; } void ClearInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 0; } + ALWAYS_INLINE KAutoObject *GetClosedObject() { return m_closed_object; } + + ALWAYS_INLINE void SetClosedObject(KAutoObject *object) { + MESOSPHERE_ASSERT(object != nullptr); + + /* Set the object to destroy. */ + m_closed_object = object; + + /* Schedule destruction DPC. */ + if ((this->GetStackParameters().dpc_flags.load(std::memory_order_relaxed) & DpcFlag_PerformDestruction) == 0) { + this->RegisterDpc(DpcFlag_PerformDestruction); + } + } + + ALWAYS_INLINE void DestroyClosedObjects() { + /* Destroy all objects that have been closed. */ + if (KAutoObject *cur = m_closed_object; cur != nullptr) { + do { + /* Set our closed object as the next to close. */ + m_closed_object = cur->GetNextClosedObject(); + + /* Destroy the current object. */ + cur->Destroy(); + + /* Advance. */ + cur = m_closed_object; + } while (cur != nullptr); + + /* Clear the pending DPC. */ + this->ClearDpc(DpcFlag_PerformDestruction); + } + } + constexpr void SetDebugAttached() { m_debug_attached = true; } constexpr bool IsAttachedToDebugger() const { return m_debug_attached; } @@ -497,7 +546,7 @@ namespace ams::kern { constexpr u32 GetSuspendFlags() const { return m_suspend_allowed_flags & m_suspend_request_flags; } constexpr bool IsSuspended() const { return this->GetSuspendFlags() != 0; } - constexpr bool IsSuspendRequested(SuspendType type) const { return (m_suspend_request_flags & (1u << (ThreadState_SuspendShift + type))) != 0; } + constexpr bool IsSuspendRequested(SuspendType type) const { return (m_suspend_request_flags & (1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type)))) != 0; } constexpr bool IsSuspendRequested() const { return m_suspend_request_flags != 0; } void RequestSuspend(SuspendType type); void Resume(SuspendType type); @@ -521,7 +570,7 @@ namespace ams::kern { Result Run(); void Exit(); - void Terminate(); + Result Terminate(); ThreadState RequestTerminate(); Result Sleep(s64 timeout); @@ -589,4 +638,14 @@ namespace ams::kern { return GetCurrentThread().GetCurrentCore(); } + ALWAYS_INLINE void KAutoObject::ScheduleDestruction() { + MESOSPHERE_ASSERT_THIS(); + + /* Set our object to destroy. */ + m_next_closed_object = GetCurrentThread().GetClosedObject(); + + /* Set ourselves as the thread's next object to destroy. */ + GetCurrentThread().SetClosedObject(this); + } + } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp index a41c49694..489f0ca02 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp @@ -105,4 +105,10 @@ namespace ams::kern { } }; + /* Miscellaneous sanity checking. */ + static_assert(ams::svc::ThreadLocalRegionSize == THREAD_LOCAL_REGION_SIZE); + static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, message_buffer) == THREAD_LOCAL_REGION_MESSAGE_BUFFER); + static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, disable_count) == THREAD_LOCAL_REGION_DISABLE_COUNT); + static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, interrupt_flag) == THREAD_LOCAL_REGION_INTERRUPT_FLAG); + } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp index 831b35824..0abb0dbce 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp @@ -23,7 +23,7 @@ namespace ams::kern { class KTransferMemory final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KTransferMemory, KAutoObject); private: - TYPED_STORAGE(KPageGroup) m_page_group; + util::TypedStorage m_page_group; KProcess *m_owner; KProcessAddress m_address; KLightLock m_lock; diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h b/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h new file mode 100644 index 000000000..30d6e2c07 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#ifdef ATMOSPHERE_ARCH_ARM64 + + #include + +#else + + #error "Unknown architecture for CPU" + +#endif diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp index dddff3139..a9163b56e 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp @@ -20,7 +20,7 @@ namespace ams::kern::svc { - static constexpr size_t NumSupervisorCalls = 0x80; + static constexpr size_t NumSupervisorCalls = AMS_KERN_NUM_SUPERVISOR_CALLS; #define AMS_KERN_SVC_DECLARE_ENUM_ID(ID, RETURN_TYPE, NAME, ...) \ SvcId_##NAME = ID, diff --git a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp index e46c6285d..b5ebee323 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp @@ -262,25 +262,14 @@ namespace ams::kern::arch::arm64::cpu { __asm__ __volatile__("dc csw, %[v]" :: [v]"r"(sw_value) : "memory"); } - template - ALWAYS_INLINE void PerformCacheOperationBySetWayShared(F f) { - CacheLineIdRegisterAccessor clidr_el1; - const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency(); - const int levels_of_unification = clidr_el1.GetLevelsOfUnification(); - - for (int level = levels_of_coherency; level >= levels_of_unification; level--) { - PerformCacheOperationBySetWayImpl(level, f); - } + void StoreDataCacheBySetWay(int level) { + PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + cpu::DataSynchronizationBarrier(); } - template - ALWAYS_INLINE void PerformCacheOperationBySetWayLocal(F f) { - CacheLineIdRegisterAccessor clidr_el1; - const int levels_of_unification = clidr_el1.GetLevelsOfUnification(); - - for (int level = levels_of_unification - 1; level >= 0; level--) { - PerformCacheOperationBySetWayImpl(level, f); - } + void FlushDataCacheBySetWay(int level) { + PerformCacheOperationBySetWayImpl(level, FlushDataCacheLineBySetWayImpl); + cpu::DataSynchronizationBarrier(); } void KCacheHelperInterruptHandler::ProcessOperation() { @@ -291,12 +280,10 @@ namespace ams::kern::arch::arm64::cpu { InstructionMemoryBarrier(); break; case Operation::StoreDataCache: - PerformCacheOperationBySetWayLocal(StoreDataCacheLineBySetWayImpl); - DataSynchronizationBarrier(); + StoreDataCacheBySetWay(0); break; case Operation::FlushDataCache: - PerformCacheOperationBySetWayLocal(FlushDataCacheLineBySetWayImpl); - DataSynchronizationBarrier(); + FlushDataCacheBySetWay(0); break; } @@ -353,28 +340,80 @@ namespace ams::kern::arch::arm64::cpu { } - void FlushEntireDataCacheSharedForInit() { - return PerformCacheOperationBySetWayShared(FlushDataCacheLineBySetWayImpl); + void StoreEntireCacheForInit() { + /* Store local. */ + { + CacheLineIdRegisterAccessor clidr_el1; + const int levels_of_unification = clidr_el1.GetLevelsOfUnification(); + + for (int level = 0; level != levels_of_unification; ++level) { + PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + } + } + + /* Store shared. */ + { + CacheLineIdRegisterAccessor clidr_el1; + const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency(); + const int levels_of_unification = clidr_el1.GetLevelsOfUnification(); + + for (int level = levels_of_unification; level <= levels_of_coherency; ++level) { + PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + } + } + + /* Data synchronization barrier. */ + DataSynchronizationBarrierInnerShareable(); + + /* Invalidate instruction cache. */ + InvalidateEntireInstructionCacheLocalImpl(); + + /* Ensure local instruction consistency. */ + DataSynchronizationBarrierInnerShareable(); + InstructionMemoryBarrier(); } - void FlushEntireDataCacheLocalForInit() { - return PerformCacheOperationBySetWayLocal(FlushDataCacheLineBySetWayImpl); - } + void FlushEntireCacheForInit() { + /* Flush data cache. */ + { + /* Get levels of coherence/unificaiton. */ + CacheLineIdRegisterAccessor clidr_el1; + const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency(); - void InvalidateEntireInstructionCacheForInit() { + /* Store cache from L1 up to (level of coherence - 1). */ + for (int level = 0; level < levels_of_coherency - 1; ++level) { + PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + } + + /* Flush cache from (level of coherence - 1) down to L0. */ + for (int level = levels_of_coherency; level > 0; --level) { + PerformCacheOperationBySetWayImpl(level - 1, FlushDataCacheLineBySetWayImpl); + } + } + + /* Invalidate instruction cache. */ InvalidateEntireInstructionCacheLocalImpl(); EnsureInstructionConsistency(); - } - void StoreEntireCacheForInit() { - PerformCacheOperationBySetWayLocal(StoreDataCacheLineBySetWayImpl); - PerformCacheOperationBySetWayShared(StoreDataCacheLineBySetWayImpl); - DataSynchronizationBarrierInnerShareable(); - InvalidateEntireInstructionCacheForInit(); + /* Invalidate entire TLB. */ + InvalidateEntireTlb(); } void FlushEntireDataCache() { - return PerformCacheOperationBySetWayShared(FlushDataCacheLineBySetWayImpl); + KScopedCoreMigrationDisable dm; + + CacheLineIdRegisterAccessor clidr_el1; + const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency(); + + /* Store cache from L2 up to the level of coherence (if there's an L3 cache or greater). */ + for (int level = 2; level < levels_of_coherency; ++level) { + StoreDataCacheBySetWay(level - 1); + } + + /* Flush cache from the level of coherence down to L2. */ + for (int level = levels_of_coherency; level > 1; --level) { + FlushDataCacheBySetWay(level - 1); + } } Result InvalidateDataCache(void *addr, size_t size) { diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index 263a304ea..41adafb99 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -258,7 +258,7 @@ namespace ams::kern::arch::arm64 { { exception = ams::svc::DebugException_BreakPoint; param2 = far; - param3 = ams::svc::BreakPointType_HardwareInstruction; + param3 = ams::svc::BreakPointType_HardwareData; } break; case EsrEc_SErrorInterrupt: @@ -521,6 +521,11 @@ namespace ams::kern::arch::arm64 { { KScopedInterruptEnable ei; + /* Terminate the thread, if we should. */ + if (GetCurrentThread().IsTerminationRequested()) { + GetCurrentThread().Exit(); + } + HandleUserException(context, esr, far, afsr0, afsr1, data); } } else { diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp index 5efe21b12..40f595854 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp @@ -74,26 +74,32 @@ namespace ams::kern::arch::arm64 { /* Get the exception context. */ const KExceptionContext *e_ctx = GetExceptionContext(thread); + /* Get whether we're 64-bit. */ + const bool is_64_bit = this->Is64Bit(); + /* If general registers are requested, get them. */ if ((context_flags & ams::svc::ThreadContextFlag_General) != 0) { + /* We can always get X0-X7/R0-R7. */ + auto register_count = 8; if (!thread->IsCallingSvc() || thread->GetSvcId() == svc::SvcId_ReturnFromException) { - if (this->Is64Bit()) { - /* Get X0-X28. */ - for (auto i = 0; i <= 28; ++i) { - out->r[i] = e_ctx->x[i]; - } + if (is_64_bit) { + /* We're not in an SVC, so we can get X0-X29. */ + register_count = 29; } else { - /* Get R0-R12. */ - for (auto i = 0; i <= 12; ++i) { - out->r[i] = static_cast(e_ctx->x[i]); - } + /* We're 32-bit, so we should get R0-R12. */ + register_count = 13; } } + + /* Get the registers. */ + for (auto i = 0; i < register_count; ++i) { + out->r[i] = is_64_bit ? e_ctx->x[i] : static_cast(e_ctx->x[i]); + } } /* If control flags are requested, get them. */ if ((context_flags & ams::svc::ThreadContextFlag_Control) != 0) { - if (this->Is64Bit()) { + if (is_64_bit) { out->fp = e_ctx->x[29]; out->lr = e_ctx->x[30]; out->sp = e_ctx->sp; @@ -291,7 +297,7 @@ namespace ams::kern::arch::arm64 { /* If the breakpoint matches context id, we need to get the context id. */ if ((flags & (1ul << 21)) != 0) { /* Ensure that the breakpoint is context-aware. */ - R_UNLESS((name - ams::svc::HardwareBreakPointRegisterName_I0) <= (num_bp - num_ctx), svc::ResultNotSupported()); + R_UNLESS((name - ams::svc::HardwareBreakPointRegisterName_I0) >= (num_bp - num_ctx), svc::ResultNotSupported()); /* Check that the breakpoint does not have the mismatch bit. */ R_UNLESS((flags & (1ul << 22)) == 0, svc::ResultInvalidCombination()); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp index a554dcf3c..1a1a64a5b 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp @@ -239,14 +239,6 @@ namespace ams::kern::arch::arm64 { } } - Result KInterruptManager::ClearInterrupt(s32 irq) { - R_UNLESS(KInterruptController::IsGlobal(irq), svc::ResultOutOfRange()); - - KScopedInterruptDisable di; - KScopedSpinLock lk(this->GetGlobalInterruptLock()); - return this->ClearGlobal(irq); - } - Result KInterruptManager::ClearInterrupt(s32 irq, s32 core_id) { MESOSPHERE_UNUSED(core_id); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 188475ec0..e6a08d0e2 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -181,7 +181,7 @@ namespace ams::kern::arch::arm64 { return ResultSuccess(); } - Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { + Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { /* The input ID isn't actually used. */ MESOSPHERE_UNUSED(id); @@ -202,7 +202,7 @@ namespace ams::kern::arch::arm64 { const size_t as_width = GetAddressSpaceWidth(as_type); const KProcessAddress as_start = 0; const KProcessAddress as_end = (1ul << as_width); - R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager)); + R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager, resource_limit)); /* We succeeded! */ table_guard.Cancel(); @@ -279,9 +279,10 @@ namespace ams::kern::arch::arm64 { if (l1_entry->IsTable()) { L2PageTableEntry *l2_entry = impl.GetL2Entry(l1_entry, cur_address); if (l2_entry->IsTable()) { - KVirtualAddress l3_table = GetPageTableVirtualAddress(l2_entry->GetTable()); + const KVirtualAddress l3_table = GetPageTableVirtualAddress(l2_entry->GetTable()); if (this->GetPageTableManager().IsInPageTableHeap(l3_table)) { while (!this->GetPageTableManager().Close(l3_table, 1)) { /* ... */ } + ClearPageTable(l3_table); this->GetPageTableManager().Free(l3_table); } } @@ -292,16 +293,21 @@ namespace ams::kern::arch::arm64 { for (KProcessAddress cur_address = as_start; cur_address <= as_last; cur_address += L1BlockSize) { L1PageTableEntry *l1_entry = impl.GetL1Entry(cur_address); if (l1_entry->IsTable()) { - KVirtualAddress l2_table = GetPageTableVirtualAddress(l1_entry->GetTable()); + const KVirtualAddress l2_table = GetPageTableVirtualAddress(l1_entry->GetTable()); if (this->GetPageTableManager().IsInPageTableHeap(l2_table)) { while (!this->GetPageTableManager().Close(l2_table, 1)) { /* ... */ } + ClearPageTable(l2_table); this->GetPageTableManager().Free(l2_table); } } } /* Free the L1 table. */ - this->GetPageTableManager().Free(reinterpret_cast(impl.Finalize())); + { + const KVirtualAddress l1_table = reinterpret_cast(impl.Finalize()); + ClearPageTable(l1_table); + this->GetPageTableManager().Free(l1_table); + } /* Perform inherited finalization. */ KPageTableBase::Finalize(); @@ -556,13 +562,13 @@ namespace ams::kern::arch::arm64 { /* If we're not forcing an unmap, separate pages immediately. */ if (!force) { const size_t size = num_pages * PageSize; - R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll)); if (num_pages > 1) { const auto end_page = virt_addr + size; const auto last_page = end_page - PageSize; auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); }; - R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll)); merge_guard.Cancel(); } } @@ -1194,13 +1200,13 @@ namespace ams::kern::arch::arm64 { /* Separate pages before we change permissions. */ const size_t size = num_pages * PageSize; - R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll)); if (num_pages > 1) { const auto end_page = virt_addr + size; const auto last_page = end_page - PageSize; auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); }; - R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll)); merge_guard.Cancel(); } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s index 7d317ac60..33ece6e0f 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include #if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP) @@ -32,28 +33,28 @@ \ /* Save x0/x1/sp to the context. */ \ ldr x1, [sp, #(8 * 0)]; \ - str x1, [x0, #(8 * 0)]; \ + str x1, [x0, #(EXCEPTION_CONTEXT_X0)]; \ ldr x1, [sp, #(8 * 1)]; \ - str x1, [x0, #(8 * 1)]; \ + str x1, [x0, #(EXCEPTION_CONTEXT_X1)]; \ \ /* Save all other registers to the context. */ \ - stp x2, x3, [x0, #(8 * 2)]; \ - stp x4, x5, [x0, #(8 * 4)]; \ - stp x6, x7, [x0, #(8 * 6)]; \ - stp x8, x9, [x0, #(8 * 8)]; \ - stp x10, x11, [x0, #(8 * 10)]; \ - stp x12, x13, [x0, #(8 * 12)]; \ - stp x14, x15, [x0, #(8 * 14)]; \ - stp x16, x17, [x0, #(8 * 16)]; \ - stp x18, x19, [x0, #(8 * 18)]; \ - stp x20, x21, [x0, #(8 * 20)]; \ - stp x22, x23, [x0, #(8 * 22)]; \ - stp x24, x25, [x0, #(8 * 24)]; \ - stp x26, x27, [x0, #(8 * 26)]; \ - stp x28, x29, [x0, #(8 * 28)]; \ + stp x2, x3, [x0, #(EXCEPTION_CONTEXT_X2_X3)]; \ + stp x4, x5, [x0, #(EXCEPTION_CONTEXT_X4_X5)]; \ + stp x6, x7, [x0, #(EXCEPTION_CONTEXT_X6_X7)]; \ + stp x8, x9, [x0, #(EXCEPTION_CONTEXT_X8_X9)]; \ + stp x10, x11, [x0, #(EXCEPTION_CONTEXT_X10_X11)]; \ + stp x12, x13, [x0, #(EXCEPTION_CONTEXT_X12_X13)]; \ + stp x14, x15, [x0, #(EXCEPTION_CONTEXT_X14_X15)]; \ + stp x16, x17, [x0, #(EXCEPTION_CONTEXT_X16_X17)]; \ + stp x18, x19, [x0, #(EXCEPTION_CONTEXT_X18_X19)]; \ + stp x20, x21, [x0, #(EXCEPTION_CONTEXT_X20_X21)]; \ + stp x22, x23, [x0, #(EXCEPTION_CONTEXT_X22_X23)]; \ + stp x24, x25, [x0, #(EXCEPTION_CONTEXT_X24_X25)]; \ + stp x26, x27, [x0, #(EXCEPTION_CONTEXT_X26_X27)]; \ + stp x28, x29, [x0, #(EXCEPTION_CONTEXT_X28_X29)]; \ \ add x1, sp, #16; \ - stp x30, x1, [x0, #(8 * 30)]; \ + stp x30, x1, [x0, #(EXCEPTION_CONTEXT_X30_SP)]; \ \ /* Restore x0/x1. */ \ ldp x0, x1, [sp], #16; diff --git a/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s b/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s index ebdec0c46..c9c04f7be 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s @@ -758,7 +758,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtr w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -769,7 +768,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #4 @@ -801,7 +800,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtrh w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -812,7 +810,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #2 @@ -844,7 +842,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtrb w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -855,7 +852,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #1 diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s index c3e2a74d5..428a5e185 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include /* ams::kern::svc::CallReturnFromException64(Result result) */ .section .text._ZN3ams4kern3svc25CallReturnFromException64Ev, "ax", %progbits @@ -20,15 +21,15 @@ .type _ZN3ams4kern3svc25CallReturnFromException64Ev, %function _ZN3ams4kern3svc25CallReturnFromException64Ev: /* Save registers the SVC entry handler didn't. */ - stp x12, x13, [sp, #(8 * 12)] - stp x14, x15, [sp, #(8 * 14)] - stp x16, x17, [sp, #(8 * 16)] - str x19, [sp, #(8 * 19)] - stp x20, x21, [sp, #(8 * 20)] - stp x22, x23, [sp, #(8 * 22)] - stp x24, x25, [sp, #(8 * 24)] - stp x26, x26, [sp, #(8 * 26)] - stp x28, x29, [sp, #(8 * 28)] + stp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + stp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)] + stp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)] + str x19, [sp, #(EXCEPTION_CONTEXT_X19)] + stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)] + stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)] + stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)] + stp x26, x26, [sp, #(EXCEPTION_CONTEXT_X26_X27)] + stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)] /* Call ams::kern::arch::arm64::ReturnFromException(result). */ bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE @@ -62,7 +63,7 @@ _ZN3ams4kern3svc14RestoreContextEm: 0: /* We should handle DPC. */ /* Check the dpc flags. */ - ldrb w8, [sp, #(0x120 + 0x10)] + ldrb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w8, 1f /* We have DPC to do! */ @@ -82,32 +83,32 @@ _ZN3ams4kern3svc14RestoreContextEm: 1: /* We're done with DPC, and should return from the svc. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* Restore registers. */ - ldp x30, x8, [sp, #(8 * 30)] - ldp x9, x10, [sp, #(8 * 32)] - ldr x11, [sp, #(8 * 34)] + ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)] + ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)] msr sp_el0, x8 msr elr_el1, x9 msr spsr_el1, x10 msr tpidr_el0, x11 - ldp x0, x1, [sp, #(8 * 0)] - ldp x2, x3, [sp, #(8 * 2)] - ldp x4, x5, [sp, #(8 * 4)] - ldp x6, x7, [sp, #(8 * 6)] - ldp x8, x9, [sp, #(8 * 8)] - ldp x10, x11, [sp, #(8 * 10)] - ldp x12, x13, [sp, #(8 * 12)] - ldp x14, x15, [sp, #(8 * 14)] - ldp x16, x17, [sp, #(8 * 16)] - ldp x18, x19, [sp, #(8 * 18)] - ldp x20, x21, [sp, #(8 * 20)] - ldp x22, x23, [sp, #(8 * 22)] - ldp x24, x25, [sp, #(8 * 24)] - ldp x26, x27, [sp, #(8 * 26)] - ldp x28, x29, [sp, #(8 * 28)] + ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)] + ldp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)] + ldp x18, x19, [sp, #(EXCEPTION_CONTEXT_X18_X19)] + ldp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)] + ldp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)] + ldp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)] + ldp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)] + ldp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)] /* Return. */ - add sp, sp, #0x120 + add sp, sp, #(EXCEPTION_CONTEXT_SIZE) eret diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s index 3de6e3c88..429ccfed2 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include /* ams::kern::arch::arm64::SvcHandler64() */ .section .text._ZN3ams4kern4arch5arm6412SvcHandler64Ev, "ax", %progbits @@ -21,45 +22,45 @@ .type _ZN3ams4kern4arch5arm6412SvcHandler64Ev, %function _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Create a KExceptionContext for the exception. */ - sub sp, sp, #0x120 + sub sp, sp, #(EXCEPTION_CONTEXT_SIZE) /* Save registers needed for ReturnFromException */ - stp x9, x10, [sp, #(8 * 9)] - str x11, [sp, #(8 * 11)] - str x18, [sp, #(8 * 18)] + stp x9, x10, [sp, #(EXCEPTION_CONTEXT_X9_X10)] + str x11, [sp, #(EXCEPTION_CONTEXT_X11)] + str x18, [sp, #(EXCEPTION_CONTEXT_X18)] mrs x8, sp_el0 mrs x9, elr_el1 mrs x10, spsr_el1 mrs x11, tpidr_el0 - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CUR_THREAD)] /* Save callee-saved registers. */ - stp x19, x20, [sp, #(8 * 19)] - stp x21, x22, [sp, #(8 * 21)] - stp x23, x24, [sp, #(8 * 23)] - stp x25, x26, [sp, #(8 * 25)] - stp x27, x28, [sp, #(8 * 27)] + stp x19, x20, [sp, #(EXCEPTION_CONTEXT_X19_X20)] + stp x21, x22, [sp, #(EXCEPTION_CONTEXT_X21_X22)] + stp x23, x24, [sp, #(EXCEPTION_CONTEXT_X23_X24)] + stp x25, x26, [sp, #(EXCEPTION_CONTEXT_X25_X26)] + stp x27, x28, [sp, #(EXCEPTION_CONTEXT_X27_X28)] /* Save miscellaneous registers. */ - stp x0, x1, [sp, #(8 * 0)] - stp x2, x3, [sp, #(8 * 2)] - stp x4, x5, [sp, #(8 * 4)] - stp x6, x7, [sp, #(8 * 6)] - stp x29, x30, [sp, #(8 * 29)] - stp x8, x9, [sp, #(8 * 31)] - stp x10, x11, [sp, #(8 * 33)] + stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + stp x29, x30, [sp, #(EXCEPTION_CONTEXT_X29_X30)] + stp x8, x9, [sp, #(EXCEPTION_CONTEXT_SP_PC)] + stp x10, x11, [sp, #(EXCEPTION_CONTEXT_PSR_TPIDR)] /* Check if the SVC index is out of range. */ mrs x8, esr_el1 and x8, x8, #0xFF - cmp x8, #0x80 + cmp x8, #(AMS_KERN_NUM_SUPERVISOR_CALLS) b.ge 3f /* Check the specific SVC permission bit for allowal. */ mov x9, sp add x9, x9, x8, lsr#3 - ldrb w9, [x9, #0x120] + ldrb w9, [x9, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_SVC_PERMISSION)] and x10, x8, #0x7 lsr x10, x9, x10 tst x10, #1 @@ -67,11 +68,11 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Check if our disable count allows us to call SVCs. */ mrs x10, tpidrro_el0 - ldrh w10, [x10, #0x100] + ldrh w10, [x10, #(THREAD_LOCAL_REGION_DISABLE_COUNT)] cbz w10, 1f /* It might not, so check the stack params to see if we must not allow the SVC. */ - ldrb w10, [sp, #(0x120 + 0x14)] + ldrb w10, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_PINNED)] cbz w10, 3f 1: /* We can call the SVC. */ @@ -81,8 +82,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Note that we're calling the SVC. */ mov w10, #1 - strb w10, [sp, #(0x120 + 0x12)] - strb w8, [sp, #(0x120 + 0x11)] + strb w10, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] + strb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)] /* If we should, trace the svc entry. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -109,7 +110,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: 2: /* We completed the SVC, and we should handle DPC. */ /* Check the dpc flags. */ - ldrb w8, [sp, #(0x120 + 0x10)] + ldrb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w8, 4f /* We have DPC to do! */ @@ -129,57 +130,57 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: 3: /* Invalid SVC. */ /* Setup the context to call into HandleException. */ - stp x0, x1, [sp, #(8 * 0)] - stp x2, x3, [sp, #(8 * 2)] - stp x4, x5, [sp, #(8 * 4)] - stp x6, x7, [sp, #(8 * 6)] - stp xzr, xzr, [sp, #(8 * 8)] - stp xzr, xzr, [sp, #(8 * 10)] - stp xzr, xzr, [sp, #(8 * 12)] - stp xzr, xzr, [sp, #(8 * 14)] - stp xzr, xzr, [sp, #(8 * 16)] - str x19, [sp, #(8 * 19)] - stp x20, x21, [sp, #(8 * 20)] - stp x22, x23, [sp, #(8 * 22)] - stp x24, x25, [sp, #(8 * 24)] - stp x26, x27, [sp, #(8 * 26)] - stp x28, x29, [sp, #(8 * 28)] + stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X16_X17)] + str x19, [sp, #(EXCEPTION_CONTEXT_X19)] + stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)] + stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)] + stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)] + stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)] + stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)] /* Call ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *) */ mov x0, sp bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE /* Restore registers. */ - ldp x30, x8, [sp, #(8 * 30)] - ldp x9, x10, [sp, #(8 * 32)] - ldr x11, [sp, #(8 * 34)] + ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)] + ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)] msr sp_el0, x8 msr elr_el1, x9 msr spsr_el1, x10 msr tpidr_el0, x11 - ldp x0, x1, [sp, #(8 * 0)] - ldp x2, x3, [sp, #(8 * 2)] - ldp x4, x5, [sp, #(8 * 4)] - ldp x6, x7, [sp, #(8 * 6)] - ldp x8, x9, [sp, #(8 * 8)] - ldp x10, x11, [sp, #(8 * 10)] - ldp x12, x13, [sp, #(8 * 12)] - ldp x14, x15, [sp, #(8 * 14)] - ldp x16, x17, [sp, #(8 * 16)] - ldp x18, x19, [sp, #(8 * 18)] - ldp x20, x21, [sp, #(8 * 20)] - ldp x22, x23, [sp, #(8 * 22)] - ldp x24, x25, [sp, #(8 * 24)] - ldp x26, x27, [sp, #(8 * 26)] - ldp x28, x29, [sp, #(8 * 28)] + ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)] + ldp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)] + ldp x18, x19, [sp, #(EXCEPTION_CONTEXT_X18_X19)] + ldp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)] + ldp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)] + ldp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)] + ldp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)] + ldp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)] /* Return. */ - add sp, sp, #0x120 + add sp, sp, #(EXCEPTION_CONTEXT_SIZE) eret 4: /* Return from SVC. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* If we should, trace the svc exit. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -198,10 +199,10 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: #endif /* Restore registers. */ - ldp x30, x8, [sp, #(8 * 30)] - ldp x9, x10, [sp, #(8 * 32)] - ldr x11, [sp, #(8 * 34)] - ldr x18, [sp, #(8 * 18)] + ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)] + ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)] + ldr x18, [sp, #(EXCEPTION_CONTEXT_X18)] msr sp_el0, x8 msr elr_el1, x9 msr spsr_el1, x10 @@ -220,7 +221,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: mov x17, xzr /* Return. */ - add sp, sp, #0x120 + add sp, sp, #(EXCEPTION_CONTEXT_SIZE) eret /* ams::kern::arch::arm64::SvcHandler32() */ @@ -239,36 +240,36 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: mov w7, w7 /* Create a KExceptionContext for the exception. */ - sub sp, sp, #0x120 + sub sp, sp, #(EXCEPTION_CONTEXT_SIZE) /* Save system registers */ mrs x17, elr_el1 mrs x20, spsr_el1 mrs x19, tpidr_el0 - ldr x18, [sp, #(0x120 + 0x28)] - stp x17, x20, [sp, #(8 * 32)] - str x19, [sp, #(8 * 34)] + ldr x18, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CUR_THREAD)] + stp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + str x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)] /* Save registers. */ - stp x0, x1, [sp, #(8 * 0)] - stp x2, x3, [sp, #(8 * 2)] - stp x4, x5, [sp, #(8 * 4)] - stp x6, x7, [sp, #(8 * 6)] - stp x8, x9, [sp, #(8 * 8)] - stp x10, x11, [sp, #(8 * 10)] - stp x12, x13, [sp, #(8 * 12)] - stp x14, xzr, [sp, #(8 * 14)] + stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + stp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + stp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + stp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + stp x14, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)] /* Check if the SVC index is out of range. */ mrs x16, esr_el1 and x16, x16, #0xFF - cmp x16, #0x80 + cmp x16, #(AMS_KERN_NUM_SUPERVISOR_CALLS) b.ge 3f /* Check the specific SVC permission bit for allowal. */ mov x20, sp add x20, x20, x16, lsr#3 - ldrb w20, [x20, #0x120] + ldrb w20, [x20, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_SVC_PERMISSION)] and x17, x16, #0x7 lsr x17, x20, x17 tst x17, #1 @@ -276,11 +277,11 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: /* Check if our disable count allows us to call SVCs. */ mrs x15, tpidrro_el0 - ldrh w15, [x15, #0x100] + ldrh w15, [x15, #(THREAD_LOCAL_REGION_DISABLE_COUNT)] cbz w15, 1f /* It might not, so check the stack params to see if we must not allow the SVC. */ - ldrb w15, [sp, #(0x120 + 0x14)] + ldrb w15, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_PINNED)] cbz w15, 3f 1: /* We can call the SVC. */ @@ -290,8 +291,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: /* Note that we're calling the SVC. */ mov w15, #1 - strb w15, [sp, #(0x120 + 0x12)] - strb w16, [sp, #(0x120 + 0x11)] + strb w15, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] + strb w16, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)] /* If we should, trace the svc entry. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -318,7 +319,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: 2: /* We completed the SVC, and we should handle DPC. */ /* Check the dpc flags. */ - ldrb w16, [sp, #(0x120 + 0x10)] + ldrb w16, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w16, 4f /* We have DPC to do! */ @@ -338,45 +339,45 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: 3: /* Invalid SVC. */ /* Setup the context to call into HandleException. */ - stp x0, x1, [sp, #(8 * 0)] - stp x2, x3, [sp, #(8 * 2)] - stp x4, x5, [sp, #(8 * 4)] - stp x6, x7, [sp, #(8 * 6)] - stp xzr, xzr, [sp, #(8 * 16)] - stp xzr, xzr, [sp, #(8 * 18)] - stp xzr, xzr, [sp, #(8 * 20)] - stp xzr, xzr, [sp, #(8 * 22)] - stp xzr, xzr, [sp, #(8 * 24)] - stp xzr, xzr, [sp, #(8 * 26)] - stp xzr, xzr, [sp, #(8 * 28)] - stp xzr, xzr, [sp, #(8 * 30)] + stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X16_X17)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X18_X19)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X20_X21)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X22_X23)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X24_X25)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X26_X27)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X28_X29)] + stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X30_SP)] /* Call ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *) */ mov x0, sp bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE /* Restore registers. */ - ldp x17, x20, [sp, #(8 * 32)] - ldr x19, [sp, #(8 * 34)] + ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)] msr elr_el1, x17 msr spsr_el1, x20 msr tpidr_el0, x19 - ldp x0, x1, [sp, #(8 * 0)] - ldp x2, x3, [sp, #(8 * 2)] - ldp x4, x5, [sp, #(8 * 4)] - ldp x6, x7, [sp, #(8 * 6)] - ldp x8, x9, [sp, #(8 * 8)] - ldp x10, x11, [sp, #(8 * 10)] - ldp x12, x13, [sp, #(8 * 12)] - ldp x14, x15, [sp, #(8 * 14)] + ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)] + ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)] + ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)] + ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)] + ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)] /* Return. */ - add sp, sp, #0x120 + add sp, sp, #(EXCEPTION_CONTEXT_SIZE) eret 4: /* Return from SVC. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* If we should, trace the svc exit. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -395,16 +396,16 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: #endif /* Restore registers. */ - ldp x8, x9, [sp, #(8 * 8)] - ldp x10, x11, [sp, #(8 * 10)] - ldp x12, x13, [sp, #(8 * 12)] - ldp x14, xzr, [sp, #(8 * 14)] - ldp x17, x20, [sp, #(8 * 32)] - ldr x19, [sp, #(8 * 34)] + ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)] + ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)] + ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)] + ldp x14, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)] + ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)] + ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)] msr elr_el1, x17 msr spsr_el1, x20 msr tpidr_el0, x19 /* Return. */ - add sp, sp, #0x120 + add sp, sp, #(EXCEPTION_CONTEXT_SIZE) eret diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 76410a1fc..6c4e79a02 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -216,6 +216,12 @@ namespace ams::kern::board::nintendo::nx { return (m_value & (1u << n)); } + template + constexpr ALWAYS_INLINE u32 SelectBits() const { + constexpr u32 Mask = ((1u << Bits) | ...); + return m_value & Mask; + } + constexpr ALWAYS_INLINE bool GetBit(Bit n) const { return this->SelectBit(n) != 0; } @@ -242,12 +248,14 @@ namespace ams::kern::board::nintendo::nx { constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBit(Bit_NonSecure); } constexpr ALWAYS_INLINE bool IsWriteable() const { return this->GetBit(Bit_Writeable); } constexpr ALWAYS_INLINE bool IsReadable() const { return this->GetBit(Bit_Readable); } - constexpr ALWAYS_INLINE bool IsValid() const { return this->IsWriteable() || this->IsReadable(); } + constexpr ALWAYS_INLINE bool IsValid() const { return this->SelectBits(); } - constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); } + constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBits(); } constexpr ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() const { return (static_cast(m_value) << DevicePageBits) & PhysicalAddressMask; } + + ALWAYS_INLINE void InvalidateAttributes() { this->SetValue(m_value & ~(0xCu << 28)); } ALWAYS_INLINE void Invalidate() { this->SetValue(0); } }; @@ -526,7 +534,7 @@ namespace ams::kern::board::nintendo::nx { #if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT) { /* Clear the interrupt when we're done. */ - ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController); }; + ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController, GetCurrentCoreId()); }; /* Get and clear the interrupt status. */ u32 int_status, err_status, err_adr; @@ -847,7 +855,7 @@ namespace ams::kern::board::nintendo::nx { } /* Forcibly unmap all pages. */ - this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), true); + this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), false); /* Release all asids. */ for (size_t i = 0; i < TableCount; ++i) { @@ -1117,12 +1125,11 @@ namespace ams::kern::board::nintendo::nx { return ResultSuccess(); } - Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm) { + Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) { /* Clear the output size. */ *out_mapped_size = 0; /* Get the size, and validate the address. */ - const u64 size = pg.GetNumPages() * PageSize; MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); @@ -1130,28 +1137,33 @@ namespace ams::kern::board::nintendo::nx { R_UNLESS(this->IsFree(device_address, size), svc::ResultInvalidCurrentMemory()); /* Ensure that if we fail, we unmap anything we mapped. */ - auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, true); }; + auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, false); }; /* Iterate, mapping device pages. */ KDeviceVirtualAddress cur_addr = device_address; - for (auto it = pg.begin(); it != pg.end(); ++it) { - /* Require that we be able to map the device page. */ - R_UNLESS(IsHeapVirtualAddress(it->GetAddress()), svc::ResultInvalidCurrentMemory()); + while (true) { + /* Get the current contiguous range. */ + KPageTableBase::MemoryRange contig_range = {}; + R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + *out_mapped_size, size - *out_mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned)); - /* Get the physical address for the page. */ - const KPhysicalAddress phys_addr = GetHeapPhysicalAddress(it->GetAddress()); + /* Ensure we close the range when we're done. */ + ON_SCOPE_EXIT { contig_range.Close(); }; /* Map the device page. */ - const u64 block_size = it->GetSize(); size_t mapped_size = 0; - R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, phys_addr, block_size, cur_addr, device_perm)); + R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, GetHeapPhysicalAddress(contig_range.address), contig_range.size, cur_addr, device_perm)); /* Advance. */ - cur_addr += block_size; + cur_addr += contig_range.size; *out_mapped_size += mapped_size; /* If we didn't map as much as we wanted, break. */ - if (mapped_size < block_size) { + if (mapped_size < contig_range.size) { + break; + } + + /* Similarly, if we're done, break. */ + if (*out_mapped_size >= size) { break; } } @@ -1186,8 +1198,6 @@ namespace ams::kern::board::nintendo::nx { /* Check if there's nothing mapped at l1. */ if (l1 == nullptr || !l1[l1_index].IsValid()) { - MESOSPHERE_ASSERT(force); - const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); @@ -1201,30 +1211,12 @@ namespace ams::kern::board::nintendo::nx { const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); size_t num_closed = 0; - bool invalidated_tlb = false; + /* Invalidate the attributes of all entries. */ for (size_t i = 0; i < map_count; ++i) { if (l2[l2_index + i].IsValid()) { - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); - - /* Invalidate the entry. */ - l2[l2_index + i].Invalidate(); + l2[l2_index + i].InvalidateAttributes(); ++num_closed; - - /* Try to add the page to the group. */ - if (R_FAILED(pg.AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize))) { - /* If we can't add it for deferred close, close it now. */ - cpu::StoreDataCache(std::addressof(l2[l2_index + i]), sizeof(PageTableEntry)); - InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[l2_index + i])))); - SmmuSynchronizationBarrier(); - - /* Close the page's reference. */ - mm.Close(GetHeapVirtualAddress(phys_addr), 1); - } - } else { - MESOSPHERE_ASSERT(force); } } cpu::StoreDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry)); @@ -1235,6 +1227,38 @@ namespace ams::kern::board::nintendo::nx { } SmmuSynchronizationBarrier(); + /* Close the memory manager's references to the pages. */ + { + KPhysicalAddress contig_phys_addr = Null; + size_t contig_count = 0; + for (size_t i = 0; i < map_count; ++i) { + /* Get the physical address. */ + const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); + MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); + + /* Fully invalidate the entry. */ + l2[l2_index + i].Invalidate(); + + if (contig_count == 0) { + /* Ensure that our address/count is valid. */ + contig_phys_addr = phys_addr; + contig_count = contig_phys_addr != Null ? 1 : 0; + } else if (phys_addr == Null || phys_addr != (contig_phys_addr + (contig_count * DevicePageSize))) { + /* If we're no longer contiguous, close the range we've been building. */ + mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize); + + contig_phys_addr = phys_addr; + contig_count = contig_phys_addr != Null ? 1 : 0; + } else { + ++contig_count; + } + } + + if (contig_count > 0) { + mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize); + } + } + /* Close the pages. */ if (ptm.Close(KVirtualAddress(l2), num_closed)) { /* Invalidate the l1 entry. */ @@ -1243,22 +1267,12 @@ namespace ams::kern::board::nintendo::nx { /* Synchronize. */ InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index])))); - InvalidateTlbSection(m_table_asids[l0_index], address); SmmuSynchronizationBarrier(); - /* We invalidated the tlb. */ - invalidated_tlb = true; - /* Free the l2 page. */ ptm.Free(KVirtualAddress(l2)); } - /* Invalidate the tlb if we haven't already. */ - if (!invalidated_tlb) { - InvalidateTlbSection(m_table_asids[l0_index], address); - SmmuSynchronizationBarrier(); - } - /* Advance. */ address += map_count * DevicePageSize; remaining -= map_count * DevicePageSize; @@ -1287,114 +1301,158 @@ namespace ams::kern::board::nintendo::nx { remaining -= DeviceLargePageSize; } } - - /* Close references to the pages in the group. */ - pg.Close(); } - Result KDevicePageTable::MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const { - MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0); - MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0); + bool KDevicePageTable::Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const { + MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); + MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); + + /* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */ + KPageTableBase::MemoryRange contig_range = {}; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) { + return false; + } + + /* Ensure that we close the range when we're done. */ + bool range_open = true; + ON_SCOPE_EXIT { if (range_open) { contig_range.Close(); } }; /* Walk the directory. */ - u64 remaining = size; - bool first = true; - u32 attr = 0; - while (remaining > 0) { - const size_t l0_index = (address / DeviceRegionSize); - const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize; - const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize; + KProcessAddress cur_process_address = process_address; + size_t remaining_size = size; + KPhysicalAddress cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + size_t remaining_in_range = contig_range.size; + bool first = true; + u32 first_attr = 0; + while (remaining_size > 0) { + /* Convert the device address to a series of indices. */ + const size_t l0_index = (device_address / DeviceRegionSize); + const size_t l1_index = (device_address % DeviceRegionSize) / DeviceLargePageSize; + const size_t l2_index = (device_address % DeviceLargePageSize) / DevicePageSize; /* Get and validate l1. */ const PageDirectoryEntry *l1 = GetPointer(m_tables[l0_index]); - R_UNLESS(l1 != nullptr, svc::ResultInvalidCurrentMemory()); - R_UNLESS(l1[l1_index].IsValid(), svc::ResultInvalidCurrentMemory()); + if (!(l1 != nullptr && l1[l1_index].IsValid())) { + return false; + } if (l1[l1_index].IsTable()) { /* We're acting on an l2 entry. */ const PageTableEntry *l2 = GetPointer(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress())); + /* Determine the number of pages to check. */ const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; - const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); + const size_t map_count = std::min(remaining_in_entry, remaining_size / DevicePageSize); + /* Check each page. */ for (size_t i = 0; i < map_count; ++i) { /* Ensure the l2 entry is valid. */ - R_UNLESS(l2[l2_index + i].IsValid(), svc::ResultInvalidCurrentMemory()); - - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); - - /* Add to the group. */ - R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize)); - - /* If this is our first entry, get the attribute. */ - if (first) { - attr = l2[l2_index + i].GetAttributes(); - first = false; - } else { - /* Validate the attributes match the first entry. */ - R_UNLESS(l2[l2_index + i].GetAttributes() == attr, svc::ResultInvalidCurrentMemory()); + if (!l2[l2_index + i].IsValid()) { + return false; } + + /* Check that the attributes match the first attributes we encountered. */ + const u32 cur_attr = l2[l2_index + i].GetAttributes(); + if (!first && cur_attr != first_attr) { + return false; + } + + /* If there's nothing remaining in the range, refresh the range. */ + if (remaining_in_range == 0) { + contig_range.Close(); + + range_open = false; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) { + return false; + } + range_open = true; + + cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + remaining_in_range = contig_range.size; + } + + /* Check that the physical address is expected. */ + if (l2[l2_index + i].GetPhysicalAddress() != cur_phys_address) { + return false; + } + + /* Advance. */ + cur_phys_address += DevicePageSize; + cur_process_address += DevicePageSize; + remaining_size -= DevicePageSize; + remaining_in_range -= DevicePageSize; + + first = false; + first_attr = cur_attr; } - /* Advance. */ - address += DevicePageSize * map_count; - remaining -= DevicePageSize * map_count; + /* Advance the device address. */ + device_address += map_count * DevicePageSize; } else { /* We're acting on an l1 entry. */ - R_UNLESS(l2_index == 0, svc::ResultInvalidCurrentMemory()); - R_UNLESS(remaining >= DeviceLargePageSize, svc::ResultInvalidCurrentMemory()); + if (!(l2_index == 0 && remaining_size >= DeviceLargePageSize)) { + return false; + } - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l1[l1_index].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); + /* Check that the attributes match the first attributes we encountered. */ + const u32 cur_attr = l1[l1_index].GetAttributes(); + if (!first && cur_attr != first_attr) { + return false; + } - /* Add to the group. */ - R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize)); + /* If there's nothing remaining in the range, refresh the range. */ + if (remaining_in_range == 0) { + contig_range.Close(); - /* If this is our first entry, get the attribute. */ - if (first) { - attr = l1[l1_index].GetAttributes(); - first = false; - } else { - /* Validate the attributes match the first entry. */ - R_UNLESS(l1[l1_index].GetAttributes() == attr, svc::ResultInvalidCurrentMemory()); + range_open = false; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) { + return false; + } + range_open = true; + + cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + remaining_in_range = contig_range.size; + } + + /* Check that the physical address is expected, and there's enough in the range. */ + if (remaining_in_range < DeviceLargePageSize || l1[l1_index].GetPhysicalAddress() != cur_phys_address) { + return false; } /* Advance. */ - address += DeviceLargePageSize; - remaining -= DeviceLargePageSize; + cur_phys_address += DeviceLargePageSize; + cur_process_address += DeviceLargePageSize; + remaining_size -= DeviceLargePageSize; + remaining_in_range -= DeviceLargePageSize; + + first = false; + first_attr = cur_attr; + + /* Advance the device address. */ + device_address += DeviceLargePageSize; } } - return ResultSuccess(); + /* The range is valid! */ + return true; } - bool KDevicePageTable::Compare(const KPageGroup &compare_pg, KDeviceVirtualAddress device_address) const { - /* Check whether the page group we expect for the virtual address matches the page group we're validating. */ - KPageGroup calc_pg(std::addressof(Kernel::GetBlockInfoManager())); - return (R_SUCCEEDED(this->MakePageGroup(std::addressof(calc_pg), device_address, compare_pg.GetNumPages() * PageSize))) && - calc_pg.IsEquivalentTo(compare_pg); - } - - Result KDevicePageTable::Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) { + Result KDevicePageTable::Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings) { /* Clear the output size. */ *out_mapped_size = 0; /* Map the pages. */ s32 num_pt = 0; - return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits::max(), pg, device_address, device_perm); + return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits::max(), page_table, process_address, size, device_address, device_perm, is_aligned); } - Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) { + Result KDevicePageTable::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) { /* Validate address/size. */ - const size_t size = pg.GetNumPages() * PageSize; MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); /* Ensure the page group is correct. */ - R_UNLESS(this->Compare(pg, device_address), svc::ResultInvalidCurrentMemory()); + R_UNLESS(this->Compare(page_table, process_address, size, device_address), svc::ResultInvalidCurrentMemory()); /* Unmap the pages. */ this->UnmapImpl(device_address, size, false); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp index e6860023d..3ad3abb90 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp @@ -492,7 +492,7 @@ namespace ams::kern::board::nintendo::nx { /* Wait for a request. */ { KScopedLightLock lk(g_cv_lock); - while (!(g_sleep_target_cores & target_core_mask)) { + while ((g_sleep_target_cores & target_core_mask) == 0) { g_cv.Wait(std::addressof(g_cv_lock)); } } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s index bdc23c714..d38c476fd 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s @@ -14,7 +14,6 @@ * along with this program. If not, see . */ - /* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */ #define cpuactlr_el1 s3_1_c15_c2_0 #define cpuectlr_el1 s3_1_c15_c2_1 @@ -95,9 +94,10 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm: mrs x2, tpidr_el1 stp x1, x2, [x0], #0x10 - /* Save the virtual resumption entrypoint. */ + /* Save the virtual resumption entrypoint and cntv_cval_el0. */ adr x1, 77f - stp x1, xzr, [x0], #0x10 + mrs x2, cntv_cval_el0 + stp x1, x2, [x0], #0x10 /* Get the current core id. */ mrs x0, mpidr_el1 @@ -245,12 +245,13 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm: msr tcr_el1, x1 msr mair_el1, x2 - /* Get sctlr, tpidr, and the entrypoint. */ - ldp x1, x2, [x0], #0x10 - ldp x3, xzr, [x0], #0x10 + /* Get sctlr, tpidr, the entrypoint, and cntv_cval_el0. */ + ldp x1, x2, [x0], #0x10 + ldp x3, x4, [x0], #0x10 /* Set the global context back into x18/tpidr. */ msr tpidr_el1, x2 + msr cntv_cval_el0, x4 dsb sy isb diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 783ef1904..8768e0fa1 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -21,7 +21,8 @@ namespace ams::kern::board::nintendo::nx { namespace { - constexpr size_t SecureAlignment = 128_KB; + constexpr uintptr_t DramPhysicalAddress = 0x80000000; + constexpr size_t SecureAlignment = 128_KB; /* Global variables for panic. */ constinit bool g_call_smc_on_panic; @@ -41,8 +42,8 @@ namespace ams::kern::board::nintendo::nx { /* Nintendo uses std::mt19937_t for randomness. */ /* To save space (and because mt19337_t isn't secure anyway), */ /* We will use TinyMT. */ - bool g_initialized_random_generator; - util::TinyMT g_random_generator; + constinit bool g_initialized_random_generator; + constinit util::TinyMT g_random_generator; constinit KSpinLock g_random_lock; ALWAYS_INLINE size_t GetRealMemorySizeForInit() { @@ -89,13 +90,10 @@ namespace ams::kern::board::nintendo::nx { return value; } - void EnsureRandomGeneratorInitialized() { - if (AMS_UNLIKELY(!g_initialized_random_generator)) { - u64 seed; - smc::GenerateRandomBytes(&seed, sizeof(seed)); - g_random_generator.Initialize(reinterpret_cast(&seed), sizeof(seed) / sizeof(u32)); - g_initialized_random_generator = true; - } + ALWAYS_INLINE u64 GenerateRandomU64FromSmc() { + u64 value; + smc::GenerateRandomBytes(std::addressof(value), sizeof(value)); + return value; } ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() { @@ -348,6 +346,10 @@ namespace ams::kern::board::nintendo::nx { } } + KPhysicalAddress KSystemControl::Init::GetInitialProcessBinaryPhysicalAddress() { + return GetKernelPhysicalBaseAddress(DramPhysicalAddress) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax; + } + bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { return GetKernelConfigurationForInit().Get(); } @@ -368,7 +370,7 @@ namespace ams::kern::board::nintendo::nx { case smc::MemoryArrangement_6GBForAppletDev: return 3285_MB; case smc::MemoryArrangement_8GB: - return 4916_MB; + return 6964_MB; } }(); @@ -392,12 +394,12 @@ namespace ams::kern::board::nintendo::nx { case smc::MemoryArrangement_6GBForAppletDev: return 2193_MB; case smc::MemoryArrangement_8GB: - return 2193_MB; + return 562_MB; } }(); /* Return (possibly) adjusted size. */ - constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MB; + constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB; return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; } @@ -434,6 +436,14 @@ namespace ams::kern::board::nintendo::nx { /* System Initialization. */ void KSystemControl::InitializePhase1() { + /* Initialize our random generator. */ + { + u64 seed; + smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed)); + g_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + g_initialized_random_generator = true; + } + /* Set IsDebugMode. */ { KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode)); @@ -539,18 +549,23 @@ namespace ams::kern::board::nintendo::nx { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(g_random_lock); - EnsureRandomGeneratorInitialized(); - return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator); + if (AMS_LIKELY(g_initialized_random_generator)) { + return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator); + } else { + return GenerateUniformRange(min, max, GenerateRandomU64FromSmc); + } } u64 KSystemControl::GenerateRandomU64() { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(g_random_lock); - EnsureRandomGeneratorInitialized(); - - return GenerateRandomU64FromGenerator(); + if (AMS_LIKELY(g_initialized_random_generator)) { + return GenerateRandomU64FromGenerator(); + } else { + return GenerateRandomU64FromSmc(); + } } void KSystemControl::SleepSystem() { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index 22233b78f..757323a39 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -55,16 +55,18 @@ namespace ams::kern::board::nintendo::nx::smc { Package2Hash = 17, /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - ExosphereEmummcType = 65007, - ExospherePayloadAddress = 65008, - ExosphereLogConfiguration = 65009, + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, + ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, + ExosphereSupportedHosVersion = 65011, }; enum class SmcResult { diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index 804ab0a1e..05fc227a3 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -22,7 +22,6 @@ namespace ams::kern::init { #define FOREACH_SLAB_TYPE(HANDLER, ...) \ HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \ HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \ - HANDLER(KLinkedListNode, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ @@ -58,14 +57,14 @@ namespace ams::kern::init { /* Constexpr counts. */ constexpr size_t SlabCountKProcess = 80; constexpr size_t SlabCountKThread = 800; - constexpr size_t SlabCountKEvent = 700; + constexpr size_t SlabCountKEvent = 900; constexpr size_t SlabCountKInterruptEvent = 100; - constexpr size_t SlabCountKPort = 256; + constexpr size_t SlabCountKPort = 256 + 0x20 /* Extra 0x20 ports over Nintendo for homebrew. */; constexpr size_t SlabCountKSharedMemory = 80; constexpr size_t SlabCountKTransferMemory = 200; constexpr size_t SlabCountKCodeMemory = 10; constexpr size_t SlabCountKDeviceAddressSpace = 300; - constexpr size_t SlabCountKSession = 933; + constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; constexpr size_t SlabCountKResourceLimit = 5; @@ -77,13 +76,13 @@ namespace ams::kern::init { namespace test { - constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + sizeof(KLinkedListNode) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); + constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize); } /* Global to hold our resource counts. */ - KSlabResourceCounts g_slab_resource_counts = { + constinit KSlabResourceCounts g_slab_resource_counts = { .num_KProcess = SlabCountKProcess, .num_KThread = SlabCountKThread, .num_KEvent = SlabCountKEvent, @@ -132,7 +131,9 @@ namespace ams::kern::init { } size_t CalculateSlabHeapGapSize() { - return (kern::GetTargetFirmware() >= TargetFirmware_10_0_0) ? KernelSlabHeapGapsSize : KernelSlabHeapGapsSizeDeprecated; + constexpr size_t KernelSlabHeapGapSize = 2_MB - 296_KB; + static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); + return KernelSlabHeapGapSize; } size_t CalculateTotalSlabHeapSize() { diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index 1b526ca6d..2e1507a43 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -25,101 +25,225 @@ namespace ams::kern { s32 priority; }; - KVirtualAddress GetInitialProcessBinaryAddress() { - const uintptr_t end_address = KMemoryLayout::GetPageTableHeapRegion().GetEndAddress(); - MESOSPHERE_ABORT_UNLESS(end_address != 0); - return end_address - InitialProcessBinarySizeMax; - } + constinit KVirtualAddress g_initial_process_binary_address = Null; + constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; + constinit size_t g_initial_process_secure_memory_size = 0; + constinit u64 g_initial_process_id_min = std::numeric_limits::max(); + constinit u64 g_initial_process_id_max = std::numeric_limits::min(); - void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) { - if (header->magic != InitialProcessBinaryMagic) { - *header = *GetPointer(GetInitialProcessBinaryAddress()); - } - - MESOSPHERE_ABORT_UNLESS(header->magic == InitialProcessBinaryMagic); - MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess); - } - - size_t GetProcessesSecureMemorySize(KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { - u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); - const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); - - size_t size = 0; - const size_t num_processes = header.num_processes; - for (size_t i = 0; i < num_processes; i++) { - /* Validate that we can read the current KIP. */ - MESOSPHERE_ABORT_UNLESS(current <= end); - KInitialProcessReader reader; - MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); - - /* If the process uses secure memory, account for that. */ - if (reader.UsesSecureMemory()) { - size += util::AlignUp(reader.GetSize(), PageSize); + void LoadInitialProcessBinaryHeader(KVirtualAddress virt_addr = Null) { + if (g_initial_process_binary_header.magic != InitialProcessBinaryMagic) { + /* Get the virtual address, if it's not overridden. */ + if (virt_addr == Null) { + virt_addr = GetInitialProcessBinaryAddress(); } - /* Advance the reader. */ - current += reader.GetBinarySize(); - } + /* Copy and validate the header. */ + g_initial_process_binary_header = *GetPointer(virt_addr); + MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.magic == InitialProcessBinaryMagic); + MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.num_processes <= init::GetSlabResourceCounts().num_KProcess); - return size; + /* Set the image address. */ + g_initial_process_binary_address = virt_addr; + + /* Process/calculate the secure memory size. */ + KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader); + const KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size; + const size_t num_processes = g_initial_process_binary_header.num_processes; + for (size_t i = 0; i < num_processes; ++i) { + /* Validate that we can read the current KIP. */ + MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader)); + + /* Attach to the current KIP. */ + KInitialProcessReader reader; + KVirtualAddress data = reader.Attach(current); + MESOSPHERE_ABORT_UNLESS(data != Null); + + /* If the process uses secure memory, account for that. */ + if (reader.UsesSecureMemory()) { + g_initial_process_secure_memory_size += reader.GetSize() + util::AlignUp(reader.GetStackSize(), PageSize); + } + + /* Advance to the next KIP. */ + current = data + reader.GetBinarySize(); + } + } } - void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { - u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); - const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); + void CreateProcesses(InitialProcessInfo *infos) { + /* Determine process image extents. */ + KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader); + KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size; /* Decide on pools to use. */ const auto unsafe_pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); const auto secure_pool = (GetTargetFirmware() >= TargetFirmware_2_0_0) ? KMemoryManager::Pool_Secure : unsafe_pool; - const size_t num_processes = header.num_processes; - for (size_t i = 0; i < num_processes; i++) { - /* Validate that we can read the current KIP. */ - MESOSPHERE_ABORT_UNLESS(current <= end); - KInitialProcessReader reader; - MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); + const size_t num_processes = g_initial_process_binary_header.num_processes; + for (size_t i = 0; i < num_processes; ++i) { + /* Validate that we can read the current KIP header. */ + MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader)); - /* Parse process parameters and reserve memory. */ + /* Attach to the current kip. */ + KInitialProcessReader reader; + KVirtualAddress data = reader.Attach(current); + MESOSPHERE_ABORT_UNLESS(data != Null); + + /* Ensure that the remainder of our parse is page aligned. */ + if (!util::IsAligned(GetInteger(data), PageSize)) { + const KVirtualAddress aligned_data = util::AlignDown(GetInteger(data), PageSize); + std::memmove(GetVoidPointer(aligned_data), GetVoidPointer(data), end - data); + + data = aligned_data; + end -= (data - aligned_data); + } + + /* If we crossed a page boundary, free the pages we're done using. */ + if (KVirtualAddress aligned_current = util::AlignDown(GetInteger(current), PageSize); aligned_current != data) { + const size_t freed_size = data - aligned_current; + Kernel::GetMemoryManager().Close(aligned_current, freed_size / PageSize); + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, freed_size); + } + + /* Parse process parameters. */ ams::svc::CreateProcessParameter params; MESOSPHERE_R_ABORT_UNLESS(reader.MakeCreateProcessParameter(std::addressof(params), true)); - MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, params.code_num_pages * PageSize)); + + /* Get the binary size for the kip. */ + const size_t binary_size = reader.GetBinarySize(); + const size_t binary_pages = binary_size / PageSize; + + /* Get the pool for both the current (compressed) image, and the decompressed process. */ + const auto src_pool = Kernel::GetMemoryManager().GetPool(data); + const auto dst_pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool; + + /* Determine the process size, and how much memory isn't already reserved. */ + const size_t process_size = params.code_num_pages * PageSize; + const size_t unreserved_size = process_size - (src_pool == dst_pool ? util::AlignDown(binary_size, PageSize) : 0); + + /* Reserve however much memory we need to reserve. */ + MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, unreserved_size)); /* Create the process. */ KProcess *new_process = nullptr; { - /* Declare page group to use for process memory. */ + /* Make page groups to represent the data. */ KPageGroup pg(std::addressof(Kernel::GetBlockInfoManager())); + KPageGroup workaround_pg(std::addressof(Kernel::GetBlockInfoManager())); - /* Allocate memory for the process. */ - auto &mm = Kernel::GetMemoryManager(); - const auto pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool; - MESOSPHERE_R_ABORT_UNLESS(mm.AllocateAndOpen(std::addressof(pg), params.code_num_pages, KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront))); - + /* Populate the page group to represent the data. */ { - /* Ensure that we do not leak pages. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Allocate the previously unreserved pages. */ + KPageGroup unreserve_pg(std::addressof(Kernel::GetBlockInfoManager())); + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(unreserve_pg), unreserved_size / PageSize, KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront))); - /* Get the temporary region. */ - const auto &temp_region = KMemoryLayout::GetTempRegion(); - MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0); + /* Add the previously reserved pages. */ + if (src_pool == dst_pool && binary_pages != 0) { + /* NOTE: Nintendo does not check the result of this operation. */ + pg.AddBlock(data, binary_pages); + } - /* Map the process's memory into the temporary region. */ - KProcessAddress temp_address = Null; - MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); - - /* Load the process. */ - MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params)); - - /* Unmap the temporary mapping. */ - MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel)); - - /* Create a KProcess object. */ - new_process = KProcess::Create(); - MESOSPHERE_ABORT_UNLESS(new_process != nullptr); - - /* Initialize the process. */ - MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool)); + /* Add the previously unreserved pages. */ + for (const auto &block : unreserve_pg) { + /* NOTE: Nintendo does not check the result of this operation. */ + pg.AddBlock(block.GetAddress(), block.GetNumPages()); + } } + MESOSPHERE_ABORT_UNLESS(pg.GetNumPages() == static_cast(params.code_num_pages)); + + /* Ensure that we do not leak pages. */ + KPageGroup *process_pg = std::addressof(pg); + ON_SCOPE_EXIT { process_pg->Close(); }; + + /* Get the temporary region. */ + const auto &temp_region = KMemoryLayout::GetTempRegion(); + MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0); + + /* Map the process's memory into the temporary region. */ + KProcessAddress temp_address = Null; + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); + + /* Setup the new page group's memory, so that we can load the process. */ + { + /* Copy the unaligned ending of the compressed binary. */ + if (const size_t unaligned_size = binary_size - util::AlignDown(binary_size, PageSize); unaligned_size != 0) { + std::memcpy(GetVoidPointer(temp_address + process_size - unaligned_size), GetVoidPointer(data + binary_size - unaligned_size), unaligned_size); + } + + /* Copy the aligned part of the compressed binary. */ + if (const size_t aligned_size = util::AlignDown(binary_size, PageSize); aligned_size != 0 && src_pool == dst_pool) { + std::memmove(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(temp_address), aligned_size); + } else { + if (src_pool != dst_pool) { + std::memcpy(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(data), aligned_size); + Kernel::GetMemoryManager().Close(data, aligned_size / PageSize); + } + } + + /* Clear the first part of the memory. */ + std::memset(GetVoidPointer(temp_address), 0, process_size - binary_size); + } + + /* Load the process. */ + MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params, temp_address + process_size - binary_size)); + + /* Unmap the temporary mapping. */ + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel)); + + /* Create a KProcess object. */ + new_process = KProcess::Create(); + MESOSPHERE_ABORT_UNLESS(new_process != nullptr); + + /* Ensure the page group is usable for the process. */ + /* If the pool is the same, we need to use the workaround page group. */ + if (src_pool == dst_pool) { + /* Allocate a new, usable group for the process. */ + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(workaround_pg), static_cast(params.code_num_pages), KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront))); + + /* Copy data from the working page group to the usable one. */ + auto work_it = pg.begin(); + MESOSPHERE_ABORT_UNLESS(work_it != pg.end()); + { + auto work_address = work_it->GetAddress(); + auto work_remaining = work_it->GetNumPages(); + for (const auto &block : workaround_pg) { + auto block_address = block.GetAddress(); + auto block_remaining = block.GetNumPages(); + while (block_remaining > 0) { + if (work_remaining == 0) { + ++work_it; + work_address = work_it->GetAddress(); + work_remaining = work_it->GetNumPages(); + } + + const size_t cur_pages = std::min(block_remaining, work_remaining); + const size_t cur_size = cur_pages * PageSize; + std::memcpy(GetVoidPointer(block_address), GetVoidPointer(work_address), cur_size); + + block_address += cur_size; + work_address += cur_size; + + block_remaining -= cur_pages; + work_remaining -= cur_pages; + } + } + + ++work_it; + } + MESOSPHERE_ABORT_UNLESS(work_it == pg.end()); + + /* We want to use the new page group. */ + process_pg = std::addressof(workaround_pg); + pg.Close(); + } + + /* Initialize the process. */ + MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, *process_pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), dst_pool, reader.IsImmortal())); + } + + /* Release the memory that was previously reserved. */ + if (const size_t aligned_bin_size = util::AlignDown(binary_size, PageSize); aligned_bin_size != 0 && src_pool != dst_pool) { + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_bin_size); } /* Set the process's memory permissions. */ @@ -137,14 +261,21 @@ namespace ams::kern { infos[i].priority = reader.GetPriority(); /* Advance the reader. */ - current += reader.GetBinarySize(); + current = data + binary_size; + } + + /* Release remaining memory used by the image. */ + { + const size_t remaining_size = util::AlignUp(GetInteger(g_initial_process_binary_address) + g_initial_process_binary_header.size, PageSize) - util::AlignDown(GetInteger(current), PageSize); + const size_t remaining_pages = remaining_size / PageSize; + Kernel::GetMemoryManager().Close(util::AlignDown(GetInteger(current), PageSize), remaining_pages); + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, remaining_size); } } - constinit KVirtualAddress g_initial_process_binary_address = Null; - constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; - constinit u64 g_initial_process_id_min = std::numeric_limits::max(); - constinit u64 g_initial_process_id_max = std::numeric_limits::min(); + ALWAYS_INLINE KVirtualAddress GetInitialProcessBinaryAddress(KVirtualAddress pool_end) { + return pool_end - InitialProcessBinarySizeMax; + } } @@ -156,49 +287,50 @@ namespace ams::kern { return g_initial_process_id_max; } - size_t GetInitialProcessesSecureMemorySize() { - LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); - - return GetProcessesSecureMemorySize(g_initial_process_binary_address != Null ? g_initial_process_binary_address : GetInitialProcessBinaryAddress(), g_initial_process_binary_header); + KVirtualAddress GetInitialProcessBinaryAddress() { + /* Get, validate the pool region. */ + const auto *pool_region = KMemoryLayout::GetVirtualMemoryRegionTree().FindLastDerived(KMemoryRegionType_VirtualDramUserPool); + MESOSPHERE_INIT_ABORT_UNLESS(pool_region != nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(pool_region->GetEndAddress() != 0); + MESOSPHERE_ABORT_UNLESS(pool_region->GetSize() >= InitialProcessBinarySizeMax); + return GetInitialProcessBinaryAddress(pool_region->GetEndAddress()); } - void CopyInitialProcessBinaryToKernelMemory() { - LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); + size_t GetInitialProcessesSecureMemorySize() { + LoadInitialProcessBinaryHeader(); + + return g_initial_process_secure_memory_size; + } + + size_t CopyInitialProcessBinaryToKernelMemory() { + LoadInitialProcessBinaryHeader(); if (g_initial_process_binary_header.num_processes > 0) { /* Reserve pages for the initial process binary from the system resource limit. */ - auto &mm = Kernel::GetMemoryManager(); const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); - const size_t num_pages = total_size / PageSize; MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); - /* Allocate memory for the image. */ - const KMemoryManager::Pool pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); - const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront); - KVirtualAddress allocated_memory = mm.AllocateAndOpenContinuous(num_pages, 1, allocate_option); - MESOSPHERE_ABORT_UNLESS(allocated_memory != Null); + /* The initial process binary is potentially over-allocated, so free any extra pages. */ + if (total_size < InitialProcessBinarySizeMax) { + Kernel::GetMemoryManager().Close(g_initial_process_binary_address + total_size, (InitialProcessBinarySizeMax - total_size) / PageSize); + } - /* Relocate the image. */ - std::memmove(GetVoidPointer(allocated_memory), GetVoidPointer(GetInitialProcessBinaryAddress()), g_initial_process_binary_header.size); - std::memset(GetVoidPointer(GetInitialProcessBinaryAddress()), 0, g_initial_process_binary_header.size); - g_initial_process_binary_address = allocated_memory; + return total_size; + } else { + return 0; } } + void LoadInitialProcessBinaryHeaderDeprecated(KPhysicalAddress pool_end) { + LoadInitialProcessBinaryHeader(GetInitialProcessBinaryAddress(KMemoryLayout::GetLinearVirtualAddress(pool_end))); + } + void CreateAndRunInitialProcesses() { /* Allocate space for the processes. */ InitialProcessInfo *infos = static_cast(__builtin_alloca(sizeof(InitialProcessInfo) * g_initial_process_binary_header.num_processes)); /* Create the processes. */ - CreateProcesses(infos, g_initial_process_binary_address, g_initial_process_binary_header); - - /* Release the memory used by the image. */ - { - const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); - const size_t num_pages = total_size / PageSize; - Kernel::GetMemoryManager().Close(g_initial_process_binary_address, num_pages); - Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, total_size); - } + CreateProcesses(infos); /* Determine the initial process id range. */ for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) { @@ -210,6 +342,7 @@ namespace ams::kern { /* Run the processes. */ for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) { MESOSPHERE_R_ABORT_UNLESS(infos[i].process->Run(infos[i].priority, infos[i].stack_size)); + infos[i].process->Close(); } } diff --git a/libraries/libmesosphere/source/kern_k_address_arbiter.cpp b/libraries/libmesosphere/source/kern_k_address_arbiter.cpp index fa554ea25..fdcefd122 100644 --- a/libraries/libmesosphere/source/kern_k_address_arbiter.cpp +++ b/libraries/libmesosphere/source/kern_k_address_arbiter.cpp @@ -51,7 +51,7 @@ namespace ams::kern { { KScopedSchedulerLock sl; - auto it = m_tree.nfind_light({ addr, -1 }); + auto it = m_tree.nfind_key({ addr, -1 }); while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { KThread *target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, ResultSuccess()); @@ -78,7 +78,7 @@ namespace ams::kern { R_UNLESS(UpdateIfEqual(std::addressof(user_value), addr, value, value + 1), svc::ResultInvalidCurrentMemory()); R_UNLESS(user_value == value, svc::ResultInvalidState()); - auto it = m_tree.nfind_light({ addr, -1 }); + auto it = m_tree.nfind_key({ addr, -1 }); while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { KThread *target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, ResultSuccess()); @@ -100,57 +100,32 @@ namespace ams::kern { { KScopedSchedulerLock sl; - auto it = m_tree.nfind_light({ addr, -1 }); + auto it = m_tree.nfind_key({ addr, -1 }); /* Determine the updated value. */ s32 new_value; - if (GetTargetFirmware() >= TargetFirmware_7_0_0) { - if (count <= 0) { - if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 2; - } else { - new_value = value + 1; - } + if (count <= 0) { + if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) { + new_value = value - 2; } else { - if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - auto tmp_it = it; - s32 tmp_num_waiters = 0; - while ((++tmp_it != m_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr)) { - if ((tmp_num_waiters++) >= count) { - break; - } - } - - if (tmp_num_waiters < count) { - new_value = value - 1; - } else { - new_value = value; - } - } else { - new_value = value + 1; - } + new_value = value + 1; } } else { - if (count <= 0) { - if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 1; - } else { - new_value = value + 1; - } - } else { + if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) { auto tmp_it = it; s32 tmp_num_waiters = 0; - while ((tmp_it != m_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && (tmp_num_waiters < count + 1)) { - ++tmp_num_waiters; - ++tmp_it; + while ((++tmp_it != m_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr)) { + if ((tmp_num_waiters++) >= count) { + break; + } } - if (tmp_num_waiters == 0) { - new_value = value + 1; - } else if (tmp_num_waiters <= count) { + if (tmp_num_waiters < count) { new_value = value - 1; } else { new_value = value; } + } else { + new_value = value + 1; } } diff --git a/libraries/libmesosphere/source/kern_k_address_space_info.cpp b/libraries/libmesosphere/source/kern_k_address_space_info.cpp index c8cce5841..425ffce08 100644 --- a/libraries/libmesosphere/source/kern_k_address_space_info.cpp +++ b/libraries/libmesosphere/source/kern_k_address_space_info.cpp @@ -22,19 +22,19 @@ namespace ams::kern { constexpr uintptr_t Invalid = std::numeric_limits::max(); constexpr KAddressSpaceInfo AddressSpaceInfos[] = { - { .bit_width = 32, .address = 2_MB, .size = 1_GB - 2_MB, .type = KAddressSpaceInfo::Type_MapSmall, }, - { .bit_width = 32, .address = 1_GB, .size = 4_GB - 1_GB, .type = KAddressSpaceInfo::Type_MapLarge, }, - { .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 36, .address = 128_MB, .size = 2_GB - 128_MB, .type = KAddressSpaceInfo::Type_MapSmall, }, - { .bit_width = 36, .address = 2_GB, .size = 64_GB - 2_GB, .type = KAddressSpaceInfo::Type_MapLarge, }, - { .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 39, .address = 128_MB, .size = 512_GB - 128_MB, .type = KAddressSpaceInfo::Type_Map39Bit, }, - { .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_MapSmall, }, - { .bit_width = 39, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 39, .address = Invalid, .size = 2_GB, .type = KAddressSpaceInfo::Type_Stack, }, + { 32, 2_MB, 1_GB - 2_MB, KAddressSpaceInfo::Type_MapSmall, }, + { 32, 1_GB, 4_GB - 1_GB, KAddressSpaceInfo::Type_MapLarge, }, + { 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Heap, }, + { 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Alias, }, + { 36, 128_MB, 2_GB - 128_MB, KAddressSpaceInfo::Type_MapSmall, }, + { 36, 2_GB, 64_GB - 2_GB, KAddressSpaceInfo::Type_MapLarge, }, + { 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, }, + { 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Alias, }, + { 39, 128_MB, 512_GB - 128_MB, KAddressSpaceInfo::Type_Map39Bit, }, + { 39, Invalid, 64_GB, KAddressSpaceInfo::Type_MapSmall, }, + { 39, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, }, + { 39, Invalid, 64_GB, KAddressSpaceInfo::Type_Alias, }, + { 39, Invalid, 2_GB, KAddressSpaceInfo::Type_Stack, }, }; constexpr bool IsAllowedIndexForAddress(size_t index) { diff --git a/libraries/libmesosphere/source/kern_k_auto_object.cpp b/libraries/libmesosphere/source/kern_k_auto_object.cpp index 9ce0e58b9..07f648e4b 100644 --- a/libraries/libmesosphere/source/kern_k_auto_object.cpp +++ b/libraries/libmesosphere/source/kern_k_auto_object.cpp @@ -22,4 +22,35 @@ namespace ams::kern { return obj; } + NOINLINE bool KAutoObject::Open() { + MESOSPHERE_ASSERT_THIS(); + + /* Atomically increment the reference count, only if it's positive. */ + u32 cur_ref_count = m_ref_count.load(std::memory_order_relaxed); + do { + if (AMS_UNLIKELY(cur_ref_count == 0)) { + MESOSPHERE_AUDIT(cur_ref_count != 0); + return false; + } + MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1); + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed)); + + return true; + } + + NOINLINE void KAutoObject::Close() { + MESOSPHERE_ASSERT_THIS(); + + /* Atomically decrement the reference count, not allowing it to become negative. */ + u32 cur_ref_count = m_ref_count.load(std::memory_order_relaxed); + do { + MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0); + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed)); + + /* If ref count hits zero, schedule the object for destruction. */ + if (cur_ref_count - 1 == 0) { + this->ScheduleDestruction(); + } + } + } diff --git a/libraries/libmesosphere/source/kern_k_client_port.cpp b/libraries/libmesosphere/source/kern_k_client_port.cpp index 963a0bafd..76ad3a373 100644 --- a/libraries/libmesosphere/source/kern_k_client_port.cpp +++ b/libraries/libmesosphere/source/kern_k_client_port.cpp @@ -42,6 +42,10 @@ namespace ams::kern { return this->GetParent()->IsLight(); } + bool KClientPort::IsServerClosed() const { + return this->GetParent()->IsServerClosed(); + } + void KClientPort::Destroy() { /* Note with our parent that we're closed. */ m_parent->OnClientClosed(); diff --git a/libraries/libmesosphere/source/kern_k_code_memory.cpp b/libraries/libmesosphere/source/kern_k_code_memory.cpp index e1b027783..8fc88e13a 100644 --- a/libraries/libmesosphere/source/kern_k_code_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_code_memory.cpp @@ -23,12 +23,11 @@ namespace ams::kern { /* Set members. */ m_owner = GetCurrentProcessPointer(); - /* Initialize the page group. */ + /* Get the owner page table. */ auto &page_table = m_owner->GetPageTable(); - new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager()); - /* Ensure that our page group's state is valid on exit. */ - auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); }; + /* Construct the page group, guarding to make sure our state is valid on exit. */ + auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager()); /* Lock the memory. */ R_TRY(page_table.LockForCodeMemory(GetPointer(m_page_group), addr, size)); diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index 3234b87d9..6ccd14794 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -102,14 +102,13 @@ namespace ams::kern { } } MESOSPHERE_ASSERT(owner_thread.IsNotNull()); - } - /* Remove the thread as a waiter from the lock owner. */ - { - KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (owner_thread != nullptr) { - owner_thread->RemoveWaiter(cur_thread); + /* Remove the thread as a waiter from the lock owner. */ + { + KScopedSchedulerLock sl; + if (KThread *mutex_owner = cur_thread->GetLockOwner(); mutex_owner != nullptr) { + mutex_owner->RemoveWaiter(cur_thread); + } } } @@ -118,7 +117,7 @@ namespace ams::kern { return cur_thread->GetWaitResult(std::addressof(dummy)); } - KThread *KConditionVariable::SignalImpl(KThread *thread) { + void KConditionVariable::SignalImpl(KThread *thread) { /* Check pre-conditions. */ MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); @@ -133,11 +132,10 @@ namespace ams::kern { can_access = cpu::CanAccessAtomic(address); if (AMS_LIKELY(can_access)) { - UpdateLockAtomic(std::addressof(prev_tag), address, own_tag, ams::svc::HandleWaitMask); + can_access = UpdateLockAtomic(std::addressof(prev_tag), address, own_tag, ams::svc::HandleWaitMask); } } - KThread *thread_to_close = nullptr; if (AMS_LIKELY(can_access)) { if (prev_tag == ams::svc::InvalidHandle) { /* If nobody held the lock previously, we're all good. */ @@ -150,7 +148,7 @@ namespace ams::kern { if (AMS_LIKELY(owner_thread != nullptr)) { /* Add the thread as a waiter on the owner. */ owner_thread->AddWaiter(thread); - thread_to_close = owner_thread; + owner_thread->Close(); } else { /* The lock was tagged with a thread that doesn't exist. */ thread->SetSyncedObject(nullptr, svc::ResultInvalidState()); @@ -162,34 +160,19 @@ namespace ams::kern { thread->SetSyncedObject(nullptr, svc::ResultInvalidCurrentMemory()); thread->Wakeup(); } - - return thread_to_close; } void KConditionVariable::Signal(uintptr_t cv_key, s32 count) { - /* Prepare for signaling. */ - constexpr int MaxThreads = 16; - KLinkedList thread_list; - KThread *thread_array[MaxThreads]; - int num_to_close = 0; - /* Perform signaling. */ int num_waiters = 0; { KScopedSchedulerLock sl; - auto it = m_tree.nfind_light({ cv_key, -1 }); + auto it = m_tree.nfind_key({ cv_key, -1 }); while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { KThread *target_thread = std::addressof(*it); - if (KThread *thread = this->SignalImpl(target_thread); thread != nullptr) { - if (num_to_close < MaxThreads) { - thread_array[num_to_close++] = thread; - } else { - thread_list.push_back(*thread); - } - } - + this->SignalImpl(target_thread); it = m_tree.erase(it); target_thread->ClearConditionVariable(); ++num_waiters; @@ -201,16 +184,6 @@ namespace ams::kern { WriteToUser(cv_key, std::addressof(has_waiter_flag)); } } - - /* Close threads in the array. */ - for (auto i = 0; i < num_to_close; ++i) { - thread_array[i]->Close(); - } - - /* Close threads in the list. */ - for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { - (*it).Close(); - } } Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) { @@ -276,11 +249,6 @@ namespace ams::kern { } } - /* Cancel the timer wait. */ - if (timer != nullptr) { - timer->CancelTask(cur_thread); - } - /* Remove from the condition variable. */ { KScopedSchedulerLock sl; @@ -295,6 +263,11 @@ namespace ams::kern { } } + /* Cancel the timer wait. */ + if (timer != nullptr) { + timer->CancelTask(cur_thread); + } + /* Get the result. */ KSynchronizationObject *dummy; return cur_thread->GetWaitResult(std::addressof(dummy)); diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index c41c87f13..dafc018c4 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -92,51 +92,7 @@ namespace ams::kern { R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is readable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserRead) == KMemoryPermission_UserRead, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserRead)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size)); } /* Advance. */ @@ -185,51 +141,7 @@ namespace ams::kern { R_TRY(target_pt.WriteDebugMemory(cur_address, GetVoidPointer(buffer), cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is writable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserReadWrite) == KMemoryPermission_UserReadWrite, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserReadWrite)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.WriteDebugIoMemory(cur_address, GetVoidPointer(buffer), cur_size)); } /* Advance. */ @@ -396,8 +308,11 @@ namespace ams::kern { } /* Send an exception event to represent our breaking the process. */ - static_assert(util::size(thread_ids) >= 4); - this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[0], thread_ids[1], thread_ids[2], thread_ids[3]); + /* TODO: How should this be handled in the case of more than 4 physical cores? */ + static_assert(util::size(thread_ids) <= 4); + [&](std::index_sequence) ALWAYS_INLINE_LAMBDA { + this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[Ix]...); + }(std::make_index_sequence()); /* Signal. */ this->NotifyAvailable(); @@ -443,6 +358,15 @@ namespace ams::kern { } else if (state == KProcess::State_DebugBreak) { /* If the process is debug breaked, transition it accordingly. */ new_state = KProcess::State_Crashed; + + /* Suspend all the threads in the process. */ + { + auto end = target->GetThreadList().end(); + for (auto it = target->GetThreadList().begin(); it != end; ++it) { + /* Request that we suspend the thread. */ + it->RequestSuspend(KThread::SuspendType_Debug); + } + } } else { /* Otherwise, don't transition. */ new_state = state; @@ -557,8 +481,12 @@ namespace ams::kern { /* Verify that the thread's svc state is valid. */ if (thread->IsCallingSvc()) { - R_UNLESS(thread->GetSvcId() != svc::SvcId_Break, svc::ResultInvalidState()); - R_UNLESS(thread->GetSvcId() != svc::SvcId_ReturnFromException, svc::ResultInvalidState()); + const u8 svc_id = thread->GetSvcId(); + + const bool is_valid_svc = svc_id == svc::SvcId_Break || + svc_id == svc::SvcId_ReturnFromException; + + R_UNLESS(is_valid_svc, svc::ResultInvalidState()); } /* Set the thread context. */ @@ -924,9 +852,6 @@ namespace ams::kern { /* If the process isn't null, detach. */ if (process.IsNotNull()) { - /* When we're done detaching, clear the reference we opened when we attached. */ - ON_SCOPE_EXIT { process->Close(); }; - /* Detach. */ { /* Lock both ourselves and the target process. */ @@ -961,6 +886,9 @@ namespace ams::kern { /* Clear our process. */ m_process = nullptr; } + + /* We're done detaching, so clear the reference we opened when we attached. */ + process->Close(); } } } diff --git a/libraries/libmesosphere/source/kern_k_device_address_space.cpp b/libraries/libmesosphere/source/kern_k_device_address_space.cpp index f9d0d23cd..2d474ef67 100644 --- a/libraries/libmesosphere/source/kern_k_device_address_space.cpp +++ b/libraries/libmesosphere/source/kern_k_device_address_space.cpp @@ -71,12 +71,11 @@ namespace ams::kern { /* Lock the address space. */ KScopedLightLock lk(m_lock); - /* Lock the pages. */ - KPageGroup pg(page_table->GetBlockInfoManager()); - R_TRY(page_table->LockForDeviceAddressSpace(std::addressof(pg), process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned)); + /* Lock the page table to prevent concurrent device mapping operations. */ + KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock(); - /* Close the pages we opened when we're done with them. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Lock the pages. */ + R_TRY(page_table->LockForMapDeviceAddressSpace(process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned)); /* Ensure that if we fail, we don't keep unmapped pages locked. */ auto unlock_guard = SCOPE_GUARD { MESOSPHERE_R_ABORT_UNLESS(page_table->UnlockForDeviceAddressSpace(process_address, size)); }; @@ -87,7 +86,7 @@ namespace ams::kern { auto mapped_size_guard = SCOPE_GUARD { *out_mapped_size = 0; }; /* Perform the mapping. */ - R_TRY(m_table.Map(out_mapped_size, pg, device_address, device_perm, refresh_mappings)); + R_TRY(m_table.Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, is_aligned, refresh_mappings)); /* Ensure that we unmap the pages if we fail to update the protections. */ /* NOTE: Nintendo does not check the result of this unmap call. */ @@ -113,19 +112,18 @@ namespace ams::kern { /* Lock the address space. */ KScopedLightLock lk(m_lock); - /* Make and open a page group for the unmapped region. */ - KPageGroup pg(page_table->GetBlockInfoManager()); - R_TRY(page_table->MakePageGroupForUnmapDeviceAddressSpace(std::addressof(pg), process_address, size)); + /* Lock the page table to prevent concurrent device mapping operations. */ + KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock(); - /* Ensure the page group is closed on scope exit. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Lock the pages. */ + R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size)); /* If we fail to unmap, we want to do a partial unlock. */ { - auto unlock_guard = SCOPE_GUARD { page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size, size); }; + auto unlock_guard = SCOPE_GUARD { MESOSPHERE_R_ABORT_UNLESS(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size, size)); }; /* Unmap. */ - R_TRY(m_table.Unmap(pg, device_address)); + R_TRY(m_table.Unmap(page_table, process_address, size, device_address)); unlock_guard.Cancel(); } diff --git a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp index 62ae50a6e..a55c30a2b 100644 --- a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp @@ -174,13 +174,20 @@ namespace ams::kern { MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled()); MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread()); - /* The only deferred procedure supported by Horizon is thread termination. */ - /* Check if we need to terminate the current thread. */ - KThread *cur_thread = GetCurrentThreadPointer(); - if (cur_thread->IsTerminationRequested()) { - KScopedInterruptEnable ei; - cur_thread->Exit(); + /* Get reference to the current thread. */ + KThread &cur_thread = GetCurrentThread(); + + /* Enable interrupts, temporarily. */ + KScopedInterruptEnable ei; + + /* If the thread is scheduled for termination, exit the thread. */ + if (cur_thread.IsTerminationRequested()) { + cur_thread.Exit(); + __builtin_unreachable(); } + + /* We may also need to destroy any closed objects. */ + cur_thread.DestroyClosedObjects(); } void KDpcManager::Sync() { diff --git a/libraries/libmesosphere/source/kern_k_dump_object.cpp b/libraries/libmesosphere/source/kern_k_dump_object.cpp index 0a9a3f86a..1c8f3cb12 100644 --- a/libraries/libmesosphere/source/kern_k_dump_object.cpp +++ b/libraries/libmesosphere/source/kern_k_dump_object.cpp @@ -345,7 +345,6 @@ namespace ams::kern::KDumpObject { DUMP_KSLABOBJ(KDebug); DUMP_KSLABOBJ(KSession); DUMP_KSLABOBJ(KLightSession); - DUMP_KSLABOBJ(KLinkedListNode); DUMP_KSLABOBJ(KThreadLocalPage); DUMP_KSLABOBJ(KObjectName); DUMP_KSLABOBJ(KEventInfo); diff --git a/libraries/libmesosphere/source/kern_k_handle_table.cpp b/libraries/libmesosphere/source/kern_k_handle_table.cpp index 7b3733fc4..8a78013d2 100644 --- a/libraries/libmesosphere/source/kern_k_handle_table.cpp +++ b/libraries/libmesosphere/source/kern_k_handle_table.cpp @@ -21,23 +21,18 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Get the table and clear our record of it. */ - Entry *saved_table = nullptr; u16 saved_table_size = 0; { KScopedDisableDispatch dd; KScopedSpinLock lk(m_lock); - std::swap(m_table, saved_table); std::swap(m_table_size, saved_table_size); } /* Close and free all entries. */ for (size_t i = 0; i < saved_table_size; i++) { - Entry *entry = std::addressof(saved_table[i]); - - if (KAutoObject *obj = entry->GetObject(); obj != nullptr) { + if (KAutoObject *obj = m_objects[i]; obj != nullptr) { obj->Close(); - this->FreeEntry(entry); } } @@ -48,12 +43,13 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Don't allow removal of a pseudo-handle. */ - if (ams::svc::IsPseudoHandle(handle)) { + if (AMS_UNLIKELY(ams::svc::IsPseudoHandle(handle))) { return false; } /* Handles must not have reserved bits set. */ - if (GetHandleBitPack(handle).Get() != 0) { + const auto handle_pack = GetHandleBitPack(handle); + if (AMS_UNLIKELY(handle_pack.Get() != 0)) { return false; } @@ -63,9 +59,11 @@ namespace ams::kern { KScopedDisableDispatch dd; KScopedSpinLock lk(m_lock); - if (Entry *entry = this->FindEntry(handle); entry != nullptr) { - obj = entry->GetObject(); - this->FreeEntry(entry); + if (AMS_LIKELY(this->IsValidHandle(handle))) { + const auto index = handle_pack.Get(); + + obj = m_objects[index]; + this->FreeEntry(index); } else { return false; } @@ -87,10 +85,14 @@ namespace ams::kern { /* Allocate entry, set output handle. */ { const auto linear_id = this->AllocateLinearId(); - Entry *entry = this->AllocateEntry(); - entry->SetUsed(obj, linear_id, type); + const auto index = this->AllocateEntry(); + + m_entry_infos[index].info = { .linear_id = linear_id, .type = type }; + m_objects[index] = obj; + obj->Open(); - *out_handle = EncodeHandle(this->GetEntryIndex(entry), linear_id); + + *out_handle = EncodeHandle(index, linear_id); } return ResultSuccess(); @@ -104,7 +106,7 @@ namespace ams::kern { /* Never exceed our capacity. */ R_UNLESS(m_count < m_table_size, svc::ResultOutOfHandles()); - *out_handle = EncodeHandle(this->GetEntryIndex(this->AllocateEntry()), this->AllocateLinearId()); + *out_handle = EncodeHandle(this->AllocateEntry(), this->AllocateLinearId()); return ResultSuccess(); } @@ -120,15 +122,13 @@ namespace ams::kern { const auto reserved = handle_pack.Get(); MESOSPHERE_ASSERT(reserved == 0); MESOSPHERE_ASSERT(linear_id != 0); - MESOSPHERE_ASSERT(index < m_table_size); MESOSPHERE_UNUSED(linear_id, reserved); - /* Free the entry. */ - /* NOTE: This code does not check the linear id. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); - - this->FreeEntry(entry); + if (AMS_LIKELY(index < m_table_size)) { + /* NOTE: This code does not check the linear id. */ + MESOSPHERE_ASSERT(m_objects[index] == nullptr); + this->FreeEntry(index); + } } void KHandleTable::Register(ams::svc::Handle handle, KAutoObject *obj, u16 type) { @@ -143,15 +143,17 @@ namespace ams::kern { const auto reserved = handle_pack.Get(); MESOSPHERE_ASSERT(reserved == 0); MESOSPHERE_ASSERT(linear_id != 0); - MESOSPHERE_ASSERT(index < m_table_size); MESOSPHERE_UNUSED(reserved); - /* Set the entry. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); + if (AMS_LIKELY(index < m_table_size)) { + /* Set the entry. */ + MESOSPHERE_ASSERT(m_objects[index] == nullptr); - entry->SetUsed(obj, linear_id, type); - obj->Open(); + m_entry_infos[index].info = { .linear_id = linear_id, .type = type }; + m_objects[index] = obj; + + obj->Open(); + } } } diff --git a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp index 6665dcdc8..83922182d 100644 --- a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp +++ b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp @@ -77,14 +77,14 @@ namespace ams::kern { Result KInitialProcessReader::MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const { /* Get and validate addresses/sizes. */ - const uintptr_t rx_address = m_kip_header->GetRxAddress(); - const size_t rx_size = m_kip_header->GetRxSize(); - const uintptr_t ro_address = m_kip_header->GetRoAddress(); - const size_t ro_size = m_kip_header->GetRoSize(); - const uintptr_t rw_address = m_kip_header->GetRwAddress(); - const size_t rw_size = m_kip_header->GetRwSize(); - const uintptr_t bss_address = m_kip_header->GetBssAddress(); - const size_t bss_size = m_kip_header->GetBssSize(); + const uintptr_t rx_address = m_kip_header.GetRxAddress(); + const size_t rx_size = m_kip_header.GetRxSize(); + const uintptr_t ro_address = m_kip_header.GetRoAddress(); + const size_t ro_size = m_kip_header.GetRoSize(); + const uintptr_t rw_address = m_kip_header.GetRwAddress(); + const size_t rw_size = m_kip_header.GetRwSize(); + const uintptr_t bss_address = m_kip_header.GetBssAddress(); + const size_t bss_size = m_kip_header.GetBssSize(); R_UNLESS(util::IsAligned(rx_address, PageSize), svc::ResultInvalidAddress()); R_UNLESS(util::IsAligned(ro_address, PageSize), svc::ResultInvalidAddress()); R_UNLESS(util::IsAligned(rw_address, PageSize), svc::ResultInvalidAddress()); @@ -115,13 +115,13 @@ namespace ams::kern { /* Set fields in parameter. */ out->code_address = map_start + start_address; out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize; - out->program_id = m_kip_header->GetProgramId(); - out->version = m_kip_header->GetVersion(); + out->program_id = m_kip_header.GetProgramId(); + out->version = m_kip_header.GetVersion(); out->flags = 0; MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize)); /* Copy name field. */ - m_kip_header->GetName(out->name, sizeof(out->name)); + m_kip_header.GetName(out->name, sizeof(out->name)); /* Apply ASLR, if needed. */ if (enable_aslr) { @@ -146,39 +146,36 @@ namespace ams::kern { return ResultSuccess(); } - Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms) const { - /* Clear memory at the address. */ - std::memset(GetVoidPointer(address), 0, params.code_num_pages * PageSize); - + Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms, KProcessAddress src) const { /* Prepare to layout the data. */ - const KProcessAddress rx_address = address + m_kip_header->GetRxAddress(); - const KProcessAddress ro_address = address + m_kip_header->GetRoAddress(); - const KProcessAddress rw_address = address + m_kip_header->GetRwAddress(); - const u8 *rx_binary = reinterpret_cast(m_kip_header + 1); - const u8 *ro_binary = rx_binary + m_kip_header->GetRxCompressedSize(); - const u8 *rw_binary = ro_binary + m_kip_header->GetRoCompressedSize(); + const KProcessAddress rx_address = address + m_kip_header.GetRxAddress(); + const KProcessAddress ro_address = address + m_kip_header.GetRoAddress(); + const KProcessAddress rw_address = address + m_kip_header.GetRwAddress(); + const u8 *rx_binary = GetPointer(src); + const u8 *ro_binary = rx_binary + m_kip_header.GetRxCompressedSize(); + const u8 *rw_binary = ro_binary + m_kip_header.GetRoCompressedSize(); /* Copy text. */ - if (util::AlignUp(m_kip_header->GetRxSize(), PageSize)) { - std::memcpy(GetVoidPointer(rx_address), rx_binary, m_kip_header->GetRxCompressedSize()); - if (m_kip_header->IsRxCompressed()) { - BlzUncompress(GetVoidPointer(rx_address + m_kip_header->GetRxCompressedSize())); + if (util::AlignUp(m_kip_header.GetRxSize(), PageSize)) { + std::memmove(GetVoidPointer(rx_address), rx_binary, m_kip_header.GetRxCompressedSize()); + if (m_kip_header.IsRxCompressed()) { + BlzUncompress(GetVoidPointer(rx_address + m_kip_header.GetRxCompressedSize())); } } /* Copy rodata. */ - if (util::AlignUp(m_kip_header->GetRoSize(), PageSize)) { - std::memcpy(GetVoidPointer(ro_address), ro_binary, m_kip_header->GetRoCompressedSize()); - if (m_kip_header->IsRoCompressed()) { - BlzUncompress(GetVoidPointer(ro_address + m_kip_header->GetRoCompressedSize())); + if (util::AlignUp(m_kip_header.GetRoSize(), PageSize)) { + std::memmove(GetVoidPointer(ro_address), ro_binary, m_kip_header.GetRoCompressedSize()); + if (m_kip_header.IsRoCompressed()) { + BlzUncompress(GetVoidPointer(ro_address + m_kip_header.GetRoCompressedSize())); } } /* Copy rwdata. */ - if (util::AlignUp(m_kip_header->GetRwSize(), PageSize)) { - std::memcpy(GetVoidPointer(rw_address), rw_binary, m_kip_header->GetRwCompressedSize()); - if (m_kip_header->IsRwCompressed()) { - BlzUncompress(GetVoidPointer(rw_address + m_kip_header->GetRwCompressedSize())); + if (util::AlignUp(m_kip_header.GetRwSize(), PageSize)) { + std::memmove(GetVoidPointer(rw_address), rw_binary, m_kip_header.GetRwCompressedSize()); + if (m_kip_header.IsRwCompressed()) { + BlzUncompress(GetVoidPointer(rw_address + m_kip_header.GetRwCompressedSize())); } } @@ -192,27 +189,27 @@ namespace ams::kern { } Result KInitialProcessReader::SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter ¶ms) const { - const size_t rx_size = m_kip_header->GetRxSize(); - const size_t ro_size = m_kip_header->GetRoSize(); - const size_t rw_size = m_kip_header->GetRwSize(); - const size_t bss_size = m_kip_header->GetBssSize(); + const size_t rx_size = m_kip_header.GetRxSize(); + const size_t ro_size = m_kip_header.GetRoSize(); + const size_t rw_size = m_kip_header.GetRwSize(); + const size_t bss_size = m_kip_header.GetBssSize(); /* Set R-X pages. */ if (rx_size) { - const uintptr_t start = m_kip_header->GetRxAddress() + params.code_address; + const uintptr_t start = m_kip_header.GetRxAddress() + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(rx_size, PageSize), ams::svc::MemoryPermission_ReadExecute)); } /* Set R-- pages. */ if (ro_size) { - const uintptr_t start = m_kip_header->GetRoAddress() + params.code_address; + const uintptr_t start = m_kip_header.GetRoAddress() + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(ro_size, PageSize), ams::svc::MemoryPermission_Read)); } /* Set RW- pages. */ if (rw_size || bss_size) { - const uintptr_t start = (rw_size ? m_kip_header->GetRwAddress() : m_kip_header->GetBssAddress()) + params.code_address; - const uintptr_t end = (bss_size ? m_kip_header->GetBssAddress() + bss_size : m_kip_header->GetRwAddress() + rw_size) + params.code_address; + const uintptr_t start = (rw_size ? m_kip_header.GetRwAddress() : m_kip_header.GetBssAddress()) + params.code_address; + const uintptr_t end = (bss_size ? m_kip_header.GetBssAddress() + bss_size : m_kip_header.GetRwAddress() + rw_size) + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(end - start, PageSize), ams::svc::MemoryPermission_ReadWrite)); } diff --git a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp index 08c46dd71..bb88a7d60 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp @@ -27,14 +27,21 @@ namespace ams::kern { Result KInterruptEvent::Initialize(int32_t interrupt_name, ams::svc::InterruptType type) { MESOSPHERE_ASSERT_THIS(); + /* Verify the interrupt is defined and global. */ + R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_name), svc::ResultOutOfRange()); + R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_name), svc::ResultOutOfRange()); + /* Set interrupt id. */ m_interrupt_id = interrupt_name; + /* Set core id. */ + m_core_id = GetCurrentCoreId(); + /* Initialize readable event base. */ KReadableEvent::Initialize(nullptr); /* Try to register the task. */ - R_TRY(KInterruptEventTask::Register(m_interrupt_id, type == ams::svc::InterruptType_Level, this)); + R_TRY(KInterruptEventTask::Register(m_interrupt_id, m_core_id, type == ams::svc::InterruptType_Level, this)); /* Mark initialized. */ m_is_initialized = true; @@ -44,7 +51,7 @@ namespace ams::kern { void KInterruptEvent::Finalize() { MESOSPHERE_ASSERT_THIS(); - g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id); + g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id, m_core_id); /* Perform inherited finalization. */ KAutoObjectWithSlabHeapAndContainer::Finalize(); @@ -60,16 +67,12 @@ namespace ams::kern { R_TRY(KReadableEvent::Reset()); /* Clear the interrupt. */ - Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id); + Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id, m_core_id); return ResultSuccess(); } - Result KInterruptEventTask::Register(s32 interrupt_id, bool level, KInterruptEvent *event) { - /* Verify the interrupt id is defined and global. */ - R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_id), svc::ResultOutOfRange()); - R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_id), svc::ResultOutOfRange()); - + Result KInterruptEventTask::Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event) { /* Lock the task table. */ KScopedLightLock lk(g_interrupt_event_lock); @@ -96,7 +99,7 @@ namespace ams::kern { KScopedLightLock tlk(task->m_lock); /* Bind the interrupt handler. */ - R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, GetCurrentCoreId(), KInterruptController::PriorityLevel_High, true, level)); + R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, core_id, KInterruptController::PriorityLevel_High, true, level)); /* Set the event. */ task->m_event = event; @@ -112,7 +115,7 @@ namespace ams::kern { return ResultSuccess(); } - void KInterruptEventTask::Unregister(s32 interrupt_id) { + void KInterruptEventTask::Unregister(s32 interrupt_id, s32 core_id) { MESOSPHERE_ASSERT_THIS(); /* Lock the task table. */ @@ -127,7 +130,7 @@ namespace ams::kern { /* Unbind the interrupt. */ m_event = nullptr; - Kernel::GetInterruptManager().UnbindHandler(interrupt_id, GetCurrentCoreId()); + Kernel::GetInterruptManager().UnbindHandler(interrupt_id, core_id); } KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) { diff --git a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp index 4c318ca88..1fe3a6492 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp @@ -83,6 +83,9 @@ namespace ams::kern { /* Do the task. */ task->DoTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/kern_k_light_lock.cpp b/libraries/libmesosphere/source/kern_k_light_lock.cpp index 06f73ea5b..74cb31aa5 100644 --- a/libraries/libmesosphere/source/kern_k_light_lock.cpp +++ b/libraries/libmesosphere/source/kern_k_light_lock.cpp @@ -35,11 +35,7 @@ namespace ams::kern { owner_thread->AddWaiter(cur_thread); /* Set thread states. */ - if (AMS_LIKELY(cur_thread->GetState() == KThread::ThreadState_Runnable)) { - cur_thread->SetState(KThread::ThreadState_Waiting); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + cur_thread->SetState(KThread::ThreadState_Waiting); if (owner_thread->IsSuspended()) { owner_thread->ContinueIfHasKernelWaiters(); @@ -49,10 +45,9 @@ namespace ams::kern { /* We're no longer waiting on the lock owner. */ { KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (AMS_UNLIKELY(owner_thread)) { + + if (KThread *owner_thread = cur_thread->GetLockOwner(); AMS_UNLIKELY(owner_thread != nullptr)) { owner_thread->RemoveWaiter(cur_thread); - KScheduler::SetSchedulerUpdateNeeded(); } } } @@ -70,17 +65,13 @@ namespace ams::kern { /* Pass the lock to the next owner. */ uintptr_t next_tag = 0; - if (next_owner) { + if (next_owner != nullptr) { next_tag = reinterpret_cast(next_owner); if (num_waiters > 1) { next_tag |= 0x1; } - if (AMS_LIKELY(next_owner->GetState() == KThread::ThreadState_Waiting)) { - next_owner->SetState(KThread::ThreadState_Runnable); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + next_owner->SetState(KThread::ThreadState_Runnable); if (next_owner->IsSuspended()) { next_owner->ContinueIfHasKernelWaiters(); diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp index 5d972d121..2a2db9319 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp @@ -25,13 +25,18 @@ namespace ams::kern { constexpr size_t CarveoutAlignment = 0x20000; constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment; + template requires (std::same_as && ...) + constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) { + return util::FromUnderlying(util::ToUnderlying(base) | (util::ToUnderlying(attr) | ...)); + } + ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() { #if defined(MESOSPHERE_DEBUG_LOG_USE_UART) switch (KSystemControl::Init::GetDebugLogUartPort()) { - case 0: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); - case 1: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); - case 2: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); - case 3: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + case 0: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap)); + case 1: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap)); + case 2: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap)); + case 3: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap)); default: return false; } #elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER) @@ -43,11 +48,11 @@ namespace ams::kern { ALWAYS_INLINE bool SetupPowerManagementControllerMemoryRegion() { /* For backwards compatibility, the PMC must remain mappable on < 2.0.0. */ - const auto rtc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : static_cast(0); - const auto pmc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : KMemoryRegionAttr_ShouldKernelMap; + const KMemoryRegionAttr rtc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : static_cast(0); + const KMemoryRegionAttr pmc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : KMemoryRegionAttr_ShouldKernelMap; - return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, KMemoryRegionType_None | rtc_restrict_attr) && - KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, KMemoryRegionType_PowerManagementController | pmc_restrict_attr); + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, GetMemoryRegionType(KMemoryRegionType_None, rtc_restrict_attr)) && + KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, GetMemoryRegionType(KMemoryRegionType_PowerManagementController, pmc_restrict_attr)); } void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) { @@ -67,29 +72,29 @@ namespace ams::kern { /* TODO: Give these constexpr defines somewhere? */ MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion()); MESOSPHERE_INIT_ABORT_UNLESS(SetupPowerManagementControllerMemoryRegion()); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController, KMemoryRegionAttr_NoUserMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController0, KMemoryRegionAttr_NoUserMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController1, KMemoryRegionAttr_NoUserMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, GetMemoryRegionType(KMemoryRegionType_InterruptDistributor, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, GetMemoryRegionType(KMemoryRegionType_InterruptCpuInterface, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap))); /* Map IRAM unconditionally, to support debug-logging-to-iram build config. */ - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x40000000, 0x40000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsIram, KMemoryRegionAttr_ShouldKernelMap))); if (GetTargetFirmware() >= TargetFirmware_2_0_0) { /* Prevent mapping the bpmp exception vectors or the ipatch region. */ - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap))); } else { /* Map devices required for legacy lps driver. */ - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_LegacyLpsExceptionVectors | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60007000, 0x1000, KMemoryRegionType_LegacyLpsFlowController | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60004000, 0x1000, KMemoryRegionType_LegacyLpsPrimaryICtlr | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60001000, 0x1000, KMemoryRegionType_LegacyLpsSemaphore | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70016000, 0x1000, KMemoryRegionType_LegacyLpsAtomics | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60006000, 0x1000, KMemoryRegionType_LegacyLpsClkRst | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsExceptionVectors, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60007000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsFlowController, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60004000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsPrimaryICtlr, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60001000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsSemaphore, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70016000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsAtomics, KMemoryRegionAttr_ShouldKernelMap))); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60006000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsClkRst, KMemoryRegionAttr_ShouldKernelMap))); } } @@ -185,6 +190,12 @@ namespace ams::kern { static_assert(KMemoryManager::Pool_Unsafe == KMemoryManager::Pool_Application); static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System); + /* NOTE: Beginning with 12.0.0 (and always, in mesosphere), the initial process binary is at the end of the pool region. */ + /* However, this is problematic for < 5.0.0, because we require the initial process binary to be parsed in order */ + /* to determine the pool sizes. Hence, we will force an initial binary load with the known pool end directly, so */ + /* that we retain compatibility with lower firmware versions. */ + LoadInitialProcessBinaryHeaderDeprecated(pool_end); + /* Get Secure pool size. */ const size_t secure_pool_size = [] ALWAYS_INLINE_LAMBDA (auto target_firmware) -> size_t { constexpr size_t LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */ diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.cpp index f2ccd9dc2..5171e1acd 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.cpp @@ -37,7 +37,7 @@ namespace ams::kern { /* Create the new region. */ KMemoryRegion *region = std::addressof(this->region_heap[this->num_regions++]); - new (region) KMemoryRegion(std::forward(args)...); + std::construct_at(region, std::forward(args)...); return region; } @@ -148,11 +148,7 @@ namespace ams::kern { } } - void KMemoryLayout::InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) { - /* Set static differences. */ - s_linear_phys_to_virt_diff = GetInteger(linear_virtual_start) - GetInteger(aligned_linear_phys_start); - s_linear_virt_to_phys_diff = GetInteger(aligned_linear_phys_start) - GetInteger(linear_virtual_start); - + void KMemoryLayout::InitializeLinearMemoryRegionTrees() { /* Initialize linear trees. */ for (auto ®ion : GetPhysicalMemoryRegionTree()) { if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { @@ -170,14 +166,8 @@ namespace ams::kern { size_t KMemoryLayout::GetResourceRegionSizeForInit() { /* Calculate resource region size based on whether we allow extra threads. */ const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); - size_t resource_region_size = KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); - /* 10.0.0 reduced the slab heap gaps by 64K. */ - if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) { - resource_region_size += (KernelSlabHeapGapsSizeDeprecated - KernelSlabHeapGapsSize); - } - - return resource_region_size; + return KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); } } diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index 7143f062c..36434ee36 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -100,19 +100,48 @@ namespace ams::kern { } /* Free each region to its corresponding heap. */ + size_t reserved_sizes[MaxManagerCount] = {}; + const uintptr_t ini_start = GetInteger(GetInitialProcessBinaryAddress()); + const uintptr_t ini_end = ini_start + InitialProcessBinarySizeMax; + const uintptr_t ini_last = ini_end - 1; for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) { if (it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) { - /* Check the region. */ - MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + /* Get the manager for the region. */ + auto &manager = m_managers[it.GetAttributes()]; - /* Free the memory to the heap. */ - m_managers[it.GetAttributes()].Free(it.GetAddress(), it.GetSize() / PageSize); + if (it.GetAddress() <= ini_start && ini_last <= it.GetLastAddress()) { + /* Free memory before the ini to the heap. */ + if (it.GetAddress() != ini_start) { + manager.Free(it.GetAddress(), (ini_start - it.GetAddress()) / PageSize); + } + + /* Open/reserve the ini memory. */ + manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); + reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; + + /* Free memory after the ini to the heap. */ + if (ini_last != it.GetLastAddress()) { + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + manager.Free(ini_end, it.GetEndAddress() - ini_end); + } + } else { + /* Ensure there's no partial overlap with the ini image. */ + if (it.GetAddress() <= ini_last) { + MESOSPHERE_ABORT_UNLESS(it.GetLastAddress() < ini_start); + } else { + /* Otherwise, check the region for general validity. */ + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + } + + /* Free the memory to the heap. */ + manager.Free(it.GetAddress(), it.GetSize() / PageSize); + } } } /* Update the used size for all managers. */ for (size_t i = 0; i < m_num_managers; ++i) { - m_managers[i].UpdateUsedHeapSize(); + m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); } } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 28a2a0386..d47fad006 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -18,6 +18,66 @@ namespace ams::kern { + namespace { + + class KScopedLightLockPair { + NON_COPYABLE(KScopedLightLockPair); + NON_MOVEABLE(KScopedLightLockPair); + private: + KLightLock *m_lower; + KLightLock *m_upper; + public: + ALWAYS_INLINE KScopedLightLockPair(KLightLock &lhs, KLightLock &rhs) { + /* Ensure our locks are in a consistent order. */ + if (std::addressof(lhs) <= std::addressof(rhs)) { + m_lower = std::addressof(lhs); + m_upper = std::addressof(rhs); + } else { + m_lower = std::addressof(rhs); + m_upper = std::addressof(lhs); + } + + /* Acquire both locks. */ + m_lower->Lock(); + if (m_lower != m_upper) { + m_upper->Lock(); + } + } + + ~KScopedLightLockPair() { + /* Unlock the upper lock. */ + if (m_upper != nullptr && m_upper != m_lower) { + m_upper->Unlock(); + } + + /* Unlock the lower lock. */ + if (m_lower != nullptr) { + m_lower->Unlock(); + } + } + public: + /* Utility. */ + ALWAYS_INLINE void TryUnlockHalf(KLightLock &lock) { + /* Only allow unlocking if the lock is half the pair. */ + if (m_lower != m_upper) { + /* We want to be sure the lock is one we own. */ + if (m_lower == std::addressof(lock)) { + lock.Unlock(); + m_lower = nullptr; + } else if (m_upper == std::addressof(lock)) { + lock.Unlock(); + m_upper = nullptr; + } + } + } + }; + + } + + void KPageTableBase::MemoryRange::Close() { + Kernel::GetMemoryManager().Close(address, size / PageSize); + } + Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) { /* Initialize our members. */ m_address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); @@ -43,9 +103,11 @@ namespace ams::kern { m_max_heap_size = 0; m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; m_memory_block_slab_manager = std::addressof(Kernel::GetSystemMemoryBlockManager()); m_block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); + m_resource_limit = std::addressof(Kernel::GetSystemResourceLimit()); m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront); m_heap_fill_value = MemoryFillValue_Zero; @@ -65,7 +127,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager) { + Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit) { /* Validate the region. */ MESOSPHERE_ABORT_UNLESS(start <= code_address); MESOSPHERE_ABORT_UNLESS(code_address < code_address + code_size); @@ -131,6 +193,7 @@ namespace ams::kern { m_is_kernel = false; m_memory_block_slab_manager = mem_block_slab_manager; m_block_info_manager = block_info_manager; + m_resource_limit = resource_limit; /* Determine the region we can place our undetermineds in. */ KProcessAddress alloc_start; @@ -227,8 +290,9 @@ namespace ams::kern { /* Set heap and fill members. */ m_current_heap_end = m_heap_region_start; m_max_heap_size = 0; - m_mapped_physical_memory_size = 0; + m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled(); m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero; @@ -283,6 +347,11 @@ namespace ams::kern { Kernel::GetUnsafeMemory().Release(m_mapped_unsafe_physical_memory); } + /* Release any ipc server memory. */ + if (m_mapped_ipc_server_memory) { + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, m_mapped_ipc_server_memory); + } + /* Invalidate the entire instruction cache. */ cpu::InvalidateEntireInstructionCache(); } @@ -361,8 +430,8 @@ namespace ams::kern { const size_t region_size = this->GetRegionSize(state); const bool is_in_region = region_start <= addr && addr < end && last <= region_start + region_size - 1; - const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr); - const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr); + const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || m_heap_region_start == m_heap_region_end); + const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || m_alias_region_start == m_alias_region_end); switch (state) { case KMemoryState_Free: case KMemoryState_Kernel: @@ -520,7 +589,7 @@ namespace ams::kern { /* Get the physical address, if we're supposed to. */ if (out_paddr != nullptr) { - MESOSPHERE_ABORT_UNLESS(this->GetPhysicalAddress(out_paddr, addr)); + MESOSPHERE_ABORT_UNLESS(this->GetPhysicalAddressLocked(out_paddr, addr)); } /* Make the page group, if we're supposed to. */ @@ -529,8 +598,9 @@ namespace ams::kern { } /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Decide on new perm and attr. */ new_perm = (new_perm != KMemoryPermission_None) ? new_perm : old_perm; @@ -585,10 +655,9 @@ namespace ams::kern { KMemoryAttribute new_attr = static_cast(old_attr & ~lock_attr); /* Create an update allocator. */ - /* NOTE: Nintendo does not initialize the allocator with any blocks. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(0)); - MESOSPHERE_UNUSED(num_allocator_blocks); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update permission, if we need to. */ if (new_perm != old_perm) { @@ -702,12 +771,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Map the memory. */ { @@ -765,12 +836,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Stack, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Unmap the memory. */ { @@ -831,12 +904,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Map the code memory. */ { @@ -940,12 +1015,14 @@ namespace ams::kern { R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), svc::ResultInvalidMemoryRegion()); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -989,7 +1066,7 @@ namespace ams::kern { KMemoryInfo info; ams::svc::PageInfo page_info; - MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(&info, &page_info, candidate)); + MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), candidate)); if (info.m_state != KMemoryState_Free) { continue; } if (!(region_start <= candidate)) { continue; } @@ -1318,6 +1395,49 @@ namespace ams::kern { return cur_block_address == GetHeapVirtualAddress(cur_addr) && cur_block_pages == (cur_size / PageSize); } + Result KPageTableBase::GetContiguousMemoryRangeWithState(MemoryRange *out, KProcessAddress address, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) { + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + auto &impl = this->GetImpl(); + + /* Begin a traversal. */ + TraversalContext context; + TraversalEntry cur_entry = {}; + R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address), svc::ResultInvalidCurrentMemory()); + + /* The region we're traversing has to be heap. */ + const KPhysicalAddress phys_address = cur_entry.phys_addr; + R_UNLESS(this->IsHeapPhysicalAddress(phys_address), svc::ResultInvalidCurrentMemory()); + + /* Traverse until we have enough size or we aren't contiguous any more. */ + size_t contig_size; + for (contig_size = cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); contig_size < size; contig_size += cur_entry.block_size) { + if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) { + break; + } + if (cur_entry.phys_addr != phys_address + contig_size) { + break; + } + } + + /* Take the minimum size for our region. */ + size = std::min(size, contig_size); + + /* Check that the memory is contiguous. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, + state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, + perm_mask, perm, + attr_mask, attr)); + + /* The memory is contiguous, so set the output range. */ + *out = { + .address = GetLinearMappedVirtualAddress(phys_address), + .size = size, + }; + + return ResultSuccess(); + } + Result KPageTableBase::SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; @@ -1335,8 +1455,9 @@ namespace ams::kern { R_SUCCEED_IF(old_perm == new_perm); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1346,7 +1467,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissions, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1391,8 +1512,9 @@ namespace ams::kern { R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1403,7 +1525,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, operation, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); /* Ensure cache coherency, if we're setting pages as executable. */ if (is_x) { @@ -1436,8 +1558,9 @@ namespace ams::kern { AttributeTestMask, KMemoryAttribute_None, ~AttributeTestMask)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1450,7 +1573,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissionsAndRefresh, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1467,9 +1590,9 @@ namespace ams::kern { KScopedLightLock lk(m_general_lock); /* Validate that setting heap size is possible at all. */ - R_UNLESS(!m_is_kernel, svc::ResultOutOfMemory()); + R_UNLESS(!m_is_kernel, svc::ResultOutOfMemory()); R_UNLESS(size <= static_cast(m_heap_region_end - m_heap_region_start), svc::ResultOutOfMemory()); - R_UNLESS(size <= m_max_heap_size, svc::ResultOutOfMemory()); + R_UNLESS(size <= m_max_heap_size, svc::ResultOutOfMemory()); if (size < static_cast(m_current_heap_end - m_heap_region_start)) { /* The size being requested is less than the current size, so we need to free the end of the heap. */ @@ -1483,8 +1606,9 @@ namespace ams::kern { KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1495,7 +1619,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Release the memory from the resource limit. */ - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); /* Apply the memory block update. */ m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, size == 0 ? KMemoryBlockDisableMergeAttribute_Normal : KMemoryBlockDisableMergeAttribute_None); @@ -1518,7 +1642,7 @@ namespace ams::kern { } /* Reserve memory for the heap extension. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the heap extension. */ @@ -1547,8 +1671,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, allocation_size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1688,10 +1813,13 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + Result KPageTableBase::MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize)); MESOSPHERE_ASSERT(util::IsAligned(size, PageSize)); MESOSPHERE_ASSERT(size > 0); + R_UNLESS(phys_addr < phys_addr + size, svc::ResultInvalidAddress()); const size_t num_pages = size / PageSize; const KPhysicalAddress last = phys_addr + size - 1; @@ -1727,12 +1855,9 @@ namespace ams::kern { region = region->GetNext(); }; - /* Lock the table. */ - KScopedLightLock lk(m_general_lock); - /* Select an address to map at. */ KProcessAddress addr = Null; - const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); + const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment); for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) { const size_t alignment = KPageTable::GetBlockSize(static_cast(block_type)); if (alignment > phys_alignment) { @@ -1750,19 +1875,34 @@ namespace ams::kern { MESOSPHERE_ASSERT(this->CanContain(addr, size, KMemoryState_Io)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); + /* Perform mapping operation. */ + const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; + R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + + /* Set the output address. */ + *out = addr; + + return ResultSuccess(); + } + + Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); - /* Perform mapping operation. */ - const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; - R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + /* Map the io memory. */ + KProcessAddress addr; + R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size, perm)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Io, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize, KMemoryState_Io, perm, KMemoryAttribute_Locked, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1799,7 +1939,7 @@ namespace ams::kern { /* Select an address to map at. */ KProcessAddress addr = Null; - const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); + const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment); for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) { const size_t alignment = KPageTable::GetBlockSize(static_cast(block_type)); if (alignment > phys_alignment) { @@ -1818,8 +1958,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1829,7 +1970,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1869,8 +2010,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1904,8 +2046,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1932,8 +2075,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, state, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1943,7 +2087,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -1966,8 +2110,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1977,7 +2122,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ *out_addr = addr; @@ -2000,8 +2145,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -2011,7 +2157,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -2036,8 +2182,9 @@ namespace ams::kern { R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), svc::ResultInvalidCurrentMemory()); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -2047,7 +2194,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -2177,7 +2324,9 @@ namespace ams::kern { /* Copy as much aligned data as we can. */ if (cur_size >= sizeof(u32)) { const size_t copy_size = util::AlignDown(cur_size, sizeof(u32)); - R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(buffer, GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), copy_size), svc::ResultInvalidPointer()); + const void * copy_src = GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)); + cpu::FlushDataCache(copy_src, copy_size); + R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(buffer, copy_src, copy_size), svc::ResultInvalidPointer()); buffer = reinterpret_cast(reinterpret_cast(buffer) + copy_size); cur_addr += copy_size; cur_size -= copy_size; @@ -2185,7 +2334,9 @@ namespace ams::kern { /* Copy remaining data. */ if (cur_size > 0) { - R_UNLESS(UserspaceAccess::CopyMemoryToUser(buffer, GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), cur_size), svc::ResultInvalidPointer()); + const void * copy_src = GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)); + cpu::FlushDataCache(copy_src, cur_size); + R_UNLESS(UserspaceAccess::CopyMemoryToUser(buffer, copy_src, cur_size), svc::ResultInvalidPointer()); } return ResultSuccess(); @@ -2312,7 +2463,169 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + Result KPageTableBase::ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserRead)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(read_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserReadWrite)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(write_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); + + /* Check that the desired range is readable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + u8 *dst = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size)); + + /* Advance. */ + address += cur_size; + dst += cur_size; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); + + /* Check that the desired range is writable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + const u8 *src = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size)); + + /* Advance. */ + address += cur_size; + src += cur_size; + } + + return ResultSuccess(); + } + + Result KPageTableBase::LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); @@ -2325,22 +2638,41 @@ namespace ams::kern { size_t num_allocator_blocks; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, test_state, test_state, perm, perm, KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None, KMemoryAttribute_DeviceShared)); - /* Make the page group, if we should. */ - if (out != nullptr) { - R_TRY(this->MakePageGroup(*out, address, num_pages)); - } - /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update the memory blocks. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::ShareToDevice, KMemoryPermission_None); - /* Open the page group. */ - if (out != nullptr) { - out->Open(); - } + return ResultSuccess(); + } + + Result KPageTableBase::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) { + /* Lightly validate the range before doing anything else. */ + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Check the memory state. */ + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_allocator_blocks), + address, size, + KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + + /* Create an update allocator. */ + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + /* Update the memory blocks. */ + const KMemoryBlockManager::MemoryBlockLockFunction lock_func = m_enable_device_address_space_merge ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, KMemoryPermission_None); return ResultSuccess(); } @@ -2362,8 +2694,9 @@ namespace ams::kern { KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update the memory blocks. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::UnshareToDevice, KMemoryPermission_None); @@ -2371,39 +2704,6 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size) { - /* Lightly validate the range before doing anything else. */ - const size_t num_pages = size / PageSize; - R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); - - /* Lock the table. */ - KScopedLightLock lk(m_general_lock); - - /* Check the memory state. */ - size_t num_allocator_blocks; - R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_allocator_blocks), - address, size, - KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); - - /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); - - /* Make the page group. */ - R_TRY(this->MakePageGroup(*out, address, num_pages)); - - /* Update the memory blocks. */ - const KMemoryBlockManager::MemoryBlockLockFunction lock_func = m_enable_device_address_space_merge ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; - m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, KMemoryPermission_None); - - /* Open a reference to the pages in the page group. */ - out->Open(); - - return ResultSuccess(); - } - Result KPageTableBase::UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; @@ -2420,32 +2720,34 @@ namespace ams::kern { size_t allocator_num_blocks = 0, unmapped_allocator_num_blocks = 0; if (unmapped_size) { if (m_enable_device_address_space_merge) { - R_TRY(this->CheckMemoryState(std::addressof(allocator_num_blocks), - address, size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(allocator_num_blocks), + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } - R_TRY(this->CheckMemoryState(std::addressof(unmapped_allocator_num_blocks), - mapped_end_address, unmapped_size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(unmapped_allocator_num_blocks), + mapped_end_address, unmapped_size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } else { - R_TRY(this->CheckMemoryState(std::addressof(allocator_num_blocks), - address, size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(allocator_num_blocks), + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } /* Create an update allocator for the region. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(allocator_num_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, allocator_num_blocks); + R_TRY(allocator_result); /* Create an update allocator for the unmapped region. */ - KMemoryBlockManagerUpdateAllocator unmapped_allocator(m_memory_block_slab_manager); - R_TRY(unmapped_allocator.Initialize(unmapped_allocator_num_blocks)); + Result unmapped_allocator_result; + KMemoryBlockManagerUpdateAllocator unmapped_allocator(std::addressof(unmapped_allocator_result), m_memory_block_slab_manager, unmapped_allocator_num_blocks); + R_TRY(unmapped_allocator_result); /* Determine parameters for the update lock call. */ KMemoryBlockManagerUpdateAllocator *lock_allocator; @@ -2479,6 +2781,41 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + const u32 test_state = KMemoryState_FlagReferenceCounted | (is_aligned ? KMemoryState_FlagCanAlignedDeviceMap : KMemoryState_FlagCanDeviceMap); + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + test_state, test_state, + perm, perm, + KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + + Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange *out, KProcessAddress address, size_t size) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { return this->LockMemoryAndOpen(nullptr, out, address, size, KMemoryState_FlagCanIpcUserBuffer, KMemoryState_FlagCanIpcUserBuffer, @@ -2533,6 +2870,23 @@ namespace ams::kern { KMemoryAttribute_Locked, std::addressof(pg)); } + Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange *out, KProcessAddress address, size_t size) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, + KMemoryPermission_UserRead, KMemoryPermission_UserRead, + KMemoryAttribute_Uncached, KMemoryAttribute_None)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + Result KPageTableBase::CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) { /* Lightly validate the range before doing anything else. */ R_UNLESS(this->Contains(src_addr, size), svc::ResultInvalidCurrentMemory()); @@ -2841,18 +3195,8 @@ namespace ams::kern { /* Copy the memory. */ { - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check memory state. */ R_TRY(src_page_table.CheckMemoryStateContiguous(src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, src_attr_mask | KMemoryAttribute_Uncached, src_attr)); @@ -2968,18 +3312,8 @@ namespace ams::kern { /* Copy the memory. */ { - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check memory state for source. */ R_TRY(src_page_table.CheckMemoryStateContiguous(src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, src_attr_mask | KMemoryAttribute_Uncached, src_attr)); @@ -3229,15 +3563,16 @@ namespace ams::kern { MESOSPHERE_ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); /* Reserve space for any partial pages we allocate. */ const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Ensure that we manage page references correctly. */ @@ -3408,18 +3743,8 @@ namespace ams::kern { /* For convenience, alias this. */ KPageTableBase &dst_page_table = *this; - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(std::addressof(src_page_table)); @@ -3429,8 +3754,9 @@ namespace ams::kern { R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(), std::addressof(num_allocator_blocks), src_addr, size, test_perm, dst_state)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(src_page_table.m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), src_page_table.m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Get the mapped extents. */ const KProcessAddress src_map_start = util::AlignUp(GetInteger(src_addr), PageSize); @@ -3460,7 +3786,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { + Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { /* Validate the address. */ R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); @@ -3472,8 +3798,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, dst_state, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -3492,12 +3819,10 @@ namespace ams::kern { m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, KMemoryState_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); /* Release from the resource limit as relevant. */ - if (auto *resource_limit = server_process->GetResourceLimit(); resource_limit != nullptr) { - const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); - const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); - const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); - } + const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); + const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); return ResultSuccess(); } @@ -3675,8 +4000,9 @@ namespace ams::kern { /* Create an update allocator. */ /* NOTE: Guaranteed zero blocks needed here. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(0)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, 0); + R_TRY(allocator_result); /* Unlock the pages. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start, mapping_size / PageSize, &KMemoryBlock::UnlockForIpc, KMemoryPermission_None); @@ -3801,7 +4127,7 @@ namespace ams::kern { /* Allocate and map the memory. */ { /* Reserve the memory from the process resource limit. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the new memory. */ @@ -3869,8 +4195,9 @@ namespace ams::kern { /* Create an update allocator. */ MESOSPHERE_ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4106,8 +4433,9 @@ namespace ams::kern { /* Create an update allocator. */ MESOSPHERE_ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4207,7 +4535,7 @@ namespace ams::kern { /* Release the memory resource. */ m_mapped_physical_memory_size -= mapped_size; - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); /* Update memory blocks. */ m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); @@ -4249,8 +4577,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4283,8 +4612,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Normal, KMemoryPermission_All, KMemoryPermission_UserReadWrite, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4306,4 +4636,108 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase &src_page_table, KProcessAddress src_address) { + /* We need to lock both this table, and the current process's table, so set up an alias. */ + KPageTableBase &dst_page_table = *this; + + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); + + /* Check that the memory is mapped in the destination process. */ + size_t num_allocator_blocks; + R_TRY(dst_page_table.CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_SharedCode, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Check that the memory is mapped in the source process. */ + R_TRY(src_page_table.CheckMemoryState(src_address, size, KMemoryState_FlagCanMapProcess, KMemoryState_FlagCanMapProcess, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Validate that the memory ranges are compatible. */ + { + /* Define a helper type. */ + struct ContiguousRangeInfo { + public: + KPageTableBase &m_pt; + TraversalContext m_context; + TraversalEntry m_entry; + KPhysicalAddress m_phys_addr; + size_t m_cur_size; + size_t m_remaining_size; + public: + ContiguousRangeInfo(KPageTableBase &pt, KProcessAddress address, size_t size) : m_pt(pt), m_remaining_size(size) { + /* Begin a traversal. */ + MESOSPHERE_ABORT_UNLESS(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry), std::addressof(m_context), address)); + + /* Setup tracking fields. */ + m_phys_addr = m_entry.phys_addr; + m_cur_size = std::min(m_remaining_size, m_entry.block_size - (GetInteger(m_phys_addr) & (m_entry.block_size - 1))); + + /* Consume the whole contiguous block. */ + this->DetermineContiguousBlockExtents(); + } + + void ContinueTraversal() { + /* Update our remaining size. */ + m_remaining_size = m_remaining_size - m_cur_size; + + /* Update our tracking fields. */ + if (m_remaining_size > 0) { + m_phys_addr = m_entry.phys_addr; + m_cur_size = std::min(m_remaining_size, m_entry.block_size); + + /* Consume the whole contiguous block. */ + this->DetermineContiguousBlockExtents(); + } + } + private: + void DetermineContiguousBlockExtents() { + /* Continue traversing until we're not contiguous, or we have enough. */ + while (m_cur_size < m_remaining_size) { + MESOSPHERE_ABORT_UNLESS(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry), std::addressof(m_context))); + + /* If we're not contiguous, we're done. */ + if (m_entry.phys_addr != m_phys_addr + m_cur_size) { + break; + } + + /* Update our current size. */ + m_cur_size = std::min(m_remaining_size, m_cur_size + m_entry.block_size); + } + } + }; + + /* Create ranges for both tables. */ + ContiguousRangeInfo src_range(src_page_table, src_address, size); + ContiguousRangeInfo dst_range(dst_page_table, dst_address, size); + + /* Validate the ranges. */ + while (src_range.m_remaining_size > 0 && dst_range.m_remaining_size > 0) { + R_UNLESS(src_range.m_phys_addr == dst_range.m_phys_addr, svc::ResultInvalidMemoryRegion()); + R_UNLESS(src_range.m_cur_size == dst_range.m_cur_size, svc::ResultInvalidMemoryRegion()); + + src_range.ContinueTraversal(); + dst_range.ContinueTraversal(); + } + } + + /* We no longer need to hold our lock on the source page table. */ + lk.TryUnlockHalf(src_page_table.m_general_lock); + + /* Create an update allocator. */ + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Unmap the memory. */ + const size_t num_pages = size / PageSize; + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); + + /* Apply the memory block update. */ + m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + + return ResultSuccess(); + } + } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 9b888f56e..ba1defba9 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -28,12 +28,19 @@ namespace ams::kern { std::atomic g_initial_process_id = InitialProcessIdMin; std::atomic g_process_id = ProcessIdMin; - void TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) { + Result TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) { /* Request that all children threads terminate. */ { KScopedLightLock proc_lk(process->GetListLock()); KScopedSchedulerLock sl; + if (thread_to_not_terminate != nullptr && process->GetPinnedThread(GetCurrentCoreId()) == thread_to_not_terminate) { + /* NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. */ + /* This is valid because the only caller which uses non-nullptr as argument uses GetCurrentThreadPointer(), */ + /* but it's still notable because it seems incorrect at first glance. */ + process->UnpinCurrentThread(); + } + auto &thread_list = process->GetThreadList(); for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { if (KThread *thread = std::addressof(*it); thread != thread_to_not_terminate) { @@ -70,9 +77,14 @@ namespace ams::kern { } /* Terminate and close the thread. */ - cur_child->Terminate(); - cur_child->Close(); + ON_SCOPE_EXIT { cur_child->Close(); }; + + if (Result terminate_result = cur_child->Terminate(); svc::ResultTerminationRequested::Includes(terminate_result)) { + return terminate_result; + } } + + return ResultSuccess(); } } @@ -206,21 +218,20 @@ namespace ams::kern { KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy)); /* Clear remaining fields. */ - m_num_threads = 0; - m_peak_num_threads = 0; - m_num_created_threads = 0; - m_num_process_switches = 0; - m_num_thread_switches = 0; - m_num_fpu_switches = 0; - m_num_supervisor_calls = 0; - m_num_ipc_messages = 0; + m_num_running_threads = 0; + m_num_process_switches = 0; + m_num_thread_switches = 0; + m_num_fpu_switches = 0; + m_num_supervisor_calls = 0; + m_num_ipc_messages = 0; - m_is_signaled = false; - m_attached_object = nullptr; - m_exception_thread = nullptr; - m_is_suspended = false; - m_memory_release_hint = 0; - m_schedule_count = 0; + m_is_signaled = false; + m_attached_object = nullptr; + m_exception_thread = nullptr; + m_is_suspended = false; + m_memory_release_hint = 0; + m_schedule_count = 0; + m_is_handle_table_initialized = false; /* We're initialized! */ m_is_initialized = true; @@ -228,7 +239,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) { + Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal) { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(res_limit != nullptr); MESOSPHERE_ABORT_UNLESS((params.code_num_pages * PageSize) / PageSize == static_cast(params.code_num_pages)); @@ -238,6 +249,7 @@ namespace ams::kern { m_resource_limit = res_limit; m_system_resource_address = Null; m_system_resource_num_pages = 0; + m_is_immortal = immortal; /* Setup page table. */ /* NOTE: Nintendo passes process ID despite not having set it yet. */ @@ -250,7 +262,7 @@ namespace ams::kern { auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager()); auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); auto *pt_manager = std::addressof(Kernel::GetPageTableManager()); - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; @@ -286,6 +298,7 @@ namespace ams::kern { /* Set pool and resource limit. */ m_memory_pool = pool; m_resource_limit = res_limit; + m_is_immortal = false; /* Get the memory sizes. */ const size_t code_num_pages = params.code_num_pages; @@ -354,7 +367,7 @@ namespace ams::kern { const auto as_type = static_cast(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask); const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) != 0; const bool enable_das_merge = (params.flags & ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge) == 0; - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; @@ -395,6 +408,11 @@ namespace ams::kern { /* Terminate child threads. */ TerminateChildren(this, nullptr); + /* Finalize the handle table, if we're not immortal. */ + if (!m_is_immortal && m_is_handle_table_initialized) { + this->FinalizeHandleTable(); + } + /* Call the debug callback. */ KDebug::OnExitProcess(this); @@ -402,29 +420,36 @@ namespace ams::kern { this->FinishTermination(); } - void KProcess::StartTermination() { - /* Terminate child threads other than the current one. */ - TerminateChildren(this, GetCurrentThreadPointer()); + Result KProcess::StartTermination() { + /* Finalize the handle table when we're done, if the process isn't immortal. */ + ON_SCOPE_EXIT { + if (!m_is_immortal) { + this->FinalizeHandleTable(); + } + }; - /* Finalize the handle tahble. */ - m_handle_table.Finalize(); + /* Terminate child threads other than the current one. */ + return TerminateChildren(this, GetCurrentThreadPointer()); } void KProcess::FinishTermination() { - /* Release resource limit hint. */ - if (m_resource_limit != nullptr) { - m_memory_release_hint = this->GetUsedUserPhysicalMemorySize(); - m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint); - } + /* Only allow termination to occur if the process isn't immortal. */ + if (!m_is_immortal) { + /* Release resource limit hint. */ + if (m_resource_limit != nullptr) { + m_memory_release_hint = this->GetUsedUserPhysicalMemorySize(); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint); + } - /* Change state. */ - { - KScopedSchedulerLock sl; - this->ChangeState(State_Terminated); - } + /* Change state. */ + { + KScopedSchedulerLock sl; + this->ChangeState(State_Terminated); + } - /* Close. */ - this->Close(); + /* Close. */ + this->Close(); + } } void KProcess::Exit() { @@ -485,16 +510,22 @@ namespace ams::kern { /* If we need to terminate, do so. */ if (needs_terminate) { /* Start termination. */ - this->StartTermination(); + if (R_SUCCEEDED(this->StartTermination())) { + /* Note for debug that we're terminating the process. */ + MESOSPHERE_LOG("KProcess::Terminate() OK pid=%ld name=%-12s\n", m_process_id, m_name); - /* Note for debug that we're terminating the process. */ - MESOSPHERE_LOG("KProcess::Terminate() pid=%ld name=%-12s\n", m_process_id, m_name); + /* Call the debug callback. */ + KDebug::OnTerminateProcess(this); - /* Call the debug callback. */ - KDebug::OnTerminateProcess(this); + /* Finish termination. */ + this->FinishTermination(); + } else { + /* Note for debug that we're terminating the process. */ + MESOSPHERE_LOG("KProcess::Terminate() FAIL pid=%ld name=%-12s\n", m_process_id, m_name); - /* Finish termination. */ - this->FinishTermination(); + /* Register the process as a work task. */ + KWorkerTaskManager::AddTask(KWorkerTaskManager::WorkerType_Exit, this); + } } return ResultSuccess(); @@ -703,19 +734,16 @@ namespace ams::kern { } } - void KProcess::IncrementThreadCount() { - MESOSPHERE_ASSERT(m_num_threads >= 0); - ++m_num_created_threads; + void KProcess::IncrementRunningThreadCount() { + MESOSPHERE_ASSERT(m_num_running_threads.load() >= 0); - if (const auto count = ++m_num_threads; count > m_peak_num_threads) { - m_peak_num_threads = count; - } + m_num_running_threads.fetch_add(1); } - void KProcess::DecrementThreadCount() { - MESOSPHERE_ASSERT(m_num_threads > 0); + void KProcess::DecrementRunningThreadCount() { + MESOSPHERE_ASSERT(m_num_running_threads.load() > 0); - if (const auto count = --m_num_threads; count == 0) { + if (m_num_running_threads.fetch_sub(1) == 1) { this->Terminate(); } } @@ -740,25 +768,22 @@ namespace ams::kern { /* If we have no exception thread, we succeeded. */ if (m_exception_thread == nullptr) { m_exception_thread = cur_thread; + KScheduler::SetSchedulerUpdateNeeded(); return true; } /* Otherwise, wait for us to not have an exception thread. */ - cur_thread->SetAddressKey(address_key); + cur_thread->SetAddressKey(address_key | 1); m_exception_thread->AddWaiter(cur_thread); - if (cur_thread->GetState() == KThread::ThreadState_Runnable) { - cur_thread->SetState(KThread::ThreadState_Waiting); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + cur_thread->SetState(KThread::ThreadState_Waiting); } + /* Remove the thread as a waiter from the lock owner. */ { KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (owner_thread != nullptr) { + + if (KThread *owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) { owner_thread->RemoveWaiter(cur_thread); - KScheduler::SetSchedulerUpdateNeeded(); } } } @@ -779,15 +804,12 @@ namespace ams::kern { /* Remove waiter thread. */ s32 num_waiters; - KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast(std::addressof(m_exception_thread))); - if (next != nullptr) { - if (next->GetState() == KThread::ThreadState_Waiting) { - next->SetState(KThread::ThreadState_Runnable); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + if (KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast(std::addressof(m_exception_thread)) | 1); next != nullptr) { + next->SetState(KThread::ThreadState_Runnable); } + KScheduler::SetSchedulerUpdateNeeded(); + return true; } else { return false; @@ -896,13 +918,13 @@ namespace ams::kern { R_TRY(m_page_table.SetMaxHeapSize(m_max_process_memory - (m_main_thread_stack_size + m_code_size))); /* Initialize our handle table. */ - R_TRY(m_handle_table.Initialize(m_capabilities.GetHandleTableSize())); - auto ht_guard = SCOPE_GUARD { m_handle_table.Finalize(); }; + R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize())); + auto ht_guard = SCOPE_GUARD { this->FinalizeHandleTable(); }; /* Create a new thread for the process. */ KThread *main_thread = KThread::Create(); R_UNLESS(main_thread != nullptr, svc::ResultOutOfResource()); - auto thread_guard = SCOPE_GUARD { main_thread->Close(); }; + ON_SCOPE_EXIT { main_thread->Close(); }; /* Initialize the thread. */ R_TRY(KThread::InitializeUserThread(main_thread, reinterpret_cast(GetVoidPointer(this->GetEntryPoint())), 0, stack_top, priority, m_ideal_core_id, this)); @@ -925,9 +947,11 @@ namespace ams::kern { /* Run our thread. */ R_TRY(main_thread->Run()); + /* Open a reference to represent that we're running. */ + this->Open(); + /* We succeeded! Cancel our guards. */ state_guard.Cancel(); - thread_guard.Cancel(); ht_guard.Cancel(); stack_guard.Cancel(); mem_reservation.Commit(); @@ -1003,12 +1027,15 @@ namespace ams::kern { const s32 core_id = GetCurrentCoreId(); KThread *cur_thread = GetCurrentThreadPointer(); - /* Pin it. */ - this->PinThread(core_id, cur_thread); - cur_thread->Pin(); + /* If the thread isn't terminated, pin it. */ + if (!cur_thread->IsTerminationRequested()) { + /* Pin it. */ + this->PinThread(core_id, cur_thread); + cur_thread->Pin(); - /* An update is needed. */ - KScheduler::SetSchedulerUpdateNeeded(); + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } } void KProcess::UnpinCurrentThread() { @@ -1026,6 +1053,20 @@ namespace ams::kern { KScheduler::SetSchedulerUpdateNeeded(); } + void KProcess::UnpinThread(KThread *thread) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Get the thread's core id. */ + const auto core_id = thread->GetActiveCore(); + + /* Unpin it. */ + this->UnpinThread(core_id, thread); + thread->Unpin(); + + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } + Result KProcess::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer out_thread_ids, s32 max_out_count) { /* Lock the list. */ KScopedLightLock lk(m_list_lock); diff --git a/libraries/libmesosphere/source/kern_k_resource_limit.cpp b/libraries/libmesosphere/source/kern_k_resource_limit.cpp index 04cf4873b..79c2c7558 100644 --- a/libraries/libmesosphere/source/kern_k_resource_limit.cpp +++ b/libraries/libmesosphere/source/kern_k_resource_limit.cpp @@ -108,6 +108,7 @@ namespace ams::kern { R_UNLESS(m_current_values[which] <= value, svc::ResultInvalidState()); m_limit_values[which] = value; + m_peak_values[which] = m_current_values[which]; return ResultSuccess(); } @@ -146,8 +147,12 @@ namespace ams::kern { if (m_current_hints[which] + value <= m_limit_values[which] && (timeout < 0 || KHardwareTimer::GetTick() < timeout)) { m_waiter_count++; - m_cond_var.Wait(&m_lock, timeout); + m_cond_var.Wait(&m_lock, timeout, false); m_waiter_count--; + + if (GetCurrentThread().IsTerminationRequested()) { + return false; + } } else { break; } diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index 9534a6e95..cce622c96 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -218,9 +218,7 @@ namespace ams::kern { KThread *task_thread = Kernel::GetInterruptTaskManager().GetThread(); { KScopedSchedulerLock sl; - if (AMS_LIKELY(task_thread->GetState() == KThread::ThreadState_Waiting)) { - task_thread->SetState(KThread::ThreadState_Runnable); - } + task_thread->SetState(KThread::ThreadState_Runnable); } } @@ -233,6 +231,10 @@ namespace ams::kern { next_thread = m_idle_thread; } + if (next_thread->GetCurrentCore() != m_core_id) { + next_thread->SetCurrentCore(m_core_id); + } + /* If we're not actually switching thread, there's nothing to do. */ if (next_thread == cur_thread) { return; @@ -265,10 +267,6 @@ namespace ams::kern { MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread); - if (next_thread->GetCurrentCore() != m_core_id) { - next_thread->SetCurrentCore(m_core_id); - } - /* Switch the current process, if we're switching processes. */ if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) { KProcess::Switch(cur_process, next_process); diff --git a/libraries/libmesosphere/source/kern_k_server_port.cpp b/libraries/libmesosphere/source/kern_k_server_port.cpp index e581a257b..1e2909240 100644 --- a/libraries/libmesosphere/source/kern_k_server_port.cpp +++ b/libraries/libmesosphere/source/kern_k_server_port.cpp @@ -40,7 +40,7 @@ namespace ams::kern { KServerSession *session = nullptr; { KScopedSchedulerLock sl; - while (!m_session_list.empty()) { + if (!m_session_list.empty()) { session = std::addressof(m_session_list.front()); m_session_list.pop_front(); } @@ -60,7 +60,7 @@ namespace ams::kern { KLightServerSession *session = nullptr; { KScopedSchedulerLock sl; - while (!m_light_session_list.empty()) { + if (!m_light_session_list.empty()) { session = std::addressof(m_light_session_list.front()); m_light_session_list.pop_front(); } diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index d2d67cbab..1cd5a1a8d 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -398,17 +398,17 @@ namespace ams::kern { /* Cleanup Send mappings. */ for (size_t i = 0; i < request->GetSendCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i))); } /* Cleanup Receive mappings. */ for (size_t i = 0; i < request->GetReceiveCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i))); } /* Cleanup Exchange mappings. */ for (size_t i = 0; i < request->GetExchangeCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i))); } return ResultSuccess(); @@ -470,7 +470,7 @@ namespace ams::kern { /* Ensure that we clean up on failure. */ auto setup_guard = SCOPE_GUARD { - dst_page_table.CleanupForIpcServer(dst_address, size, dst_state, request->GetServerProcess()); + dst_page_table.CleanupForIpcServer(dst_address, size, dst_state); src_page_table.CleanupForIpcClient(src_address, size, dst_state); }; @@ -771,6 +771,9 @@ namespace ams::kern { /* NOTE: Session is used only for debugging, and so may go unused. */ MESOSPHERE_UNUSED(session); + /* NOTE: Source page table is not used, and so may go unused. */ + MESOSPHERE_UNUSED(src_page_table); + /* Determine the message buffers. */ u32 *dst_msg_ptr, *src_msg_ptr; bool dst_user, src_user; @@ -907,7 +910,7 @@ namespace ams::kern { /* If the fast part of the copy didn't get everything, perform the slow part of the copy. */ if (fast_size < raw_size) { - R_TRY(src_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, + R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, dst_perm, KMemoryAttribute_Uncached, KMemoryAttribute_None, @@ -921,7 +924,7 @@ namespace ams::kern { constexpr KMemoryPermission DestinationPermission = static_cast(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite); /* Copy the memory. */ - R_TRY(src_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size, + R_TRY(dst_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size, KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, DestinationPermission, KMemoryAttribute_Uncached, KMemoryAttribute_None, diff --git a/libraries/libmesosphere/source/kern_k_session.cpp b/libraries/libmesosphere/source/kern_k_session.cpp index 88906469f..801be4afc 100644 --- a/libraries/libmesosphere/source/kern_k_session.cpp +++ b/libraries/libmesosphere/source/kern_k_session.cpp @@ -35,7 +35,7 @@ namespace ams::kern { m_client.Initialize(this); /* Set state and name. */ - m_state = State::Normal; + this->SetState(State::Normal); m_name = name; /* Set our owner process. */ @@ -62,8 +62,8 @@ namespace ams::kern { void KSession::OnServerClosed() { MESOSPHERE_ASSERT_THIS(); - if (m_state == State::Normal) { - m_state = State::ServerClosed; + if (this->GetState() == State::Normal) { + this->SetState(State::ServerClosed); m_client.OnServerClosed(); } } @@ -71,8 +71,8 @@ namespace ams::kern { void KSession::OnClientClosed() { MESOSPHERE_ASSERT_THIS(); - if (m_state == State::Normal) { - m_state = State::ClientClosed; + if (this->GetState() == State::Normal) { + this->SetState(State::ClientClosed); m_server.OnClientClosed(); } } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index d2d5581dd..a43f9f171 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -19,9 +19,11 @@ namespace ams::kern { namespace { + constexpr inline s32 TerminatingThreadPriority = ams::svc::SystemThreadPriorityHighest - 1; + constexpr bool IsKernelAddressKey(KProcessAddress key) { const uintptr_t key_uptr = GetInteger(key); - return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast; + return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast && (key_uptr & 1) == 0; } void InitializeKernelStack(uintptr_t stack_top) { @@ -153,8 +155,9 @@ namespace ams::kern { m_lock_owner = nullptr; m_num_core_migration_disables = 0; - /* We have no waiters, but we do have an entrypoint. */ + /* We have no waiters, and no closed objects. */ m_num_kernel_waiters = 0; + m_closed_object = nullptr; /* Set our current core id. */ m_current_core_id = phys_core; @@ -182,7 +185,6 @@ namespace ams::kern { if (owner != nullptr) { m_parent = owner; m_parent->Open(); - m_parent->IncrementThreadCount(); } /* Initialize thread context. */ @@ -310,11 +312,6 @@ namespace ams::kern { CleanupKernelStack(reinterpret_cast(m_kernel_stack_top)); } - /* Decrement the parent process's thread count. */ - if (m_parent != nullptr) { - m_parent->DecrementThreadCount(); - } - /* Perform inherited finalization. */ KAutoObjectWithSlabHeapAndContainer::Finalize(); } @@ -426,19 +423,23 @@ namespace ams::kern { if (active_core != current_core || m_physical_affinity_mask.GetAffinityMask() != m_original_physical_affinity_mask.GetAffinityMask()) { KScheduler::OnThreadAffinityMaskChanged(this, m_original_physical_affinity_mask, active_core); } + + /* Set base priority-on-unpin. */ + const s32 old_base_priority = m_base_priority; + m_base_priority_on_unpin = old_base_priority; + + /* Set base priority to higher than any possible process priority. */ + m_base_priority = std::min(old_base_priority, __builtin_ctzll(this->GetOwnerProcess()->GetPriorityMask())); + RestorePriority(this); } /* Disallow performing thread suspension. */ { /* Update our allow flags. */ - m_suspend_allowed_flags &= ~(1 << (SuspendType_Thread + ThreadState_SuspendShift)); + m_suspend_allowed_flags &= ~(1 << (util::ToUnderlying(SuspendType_Thread) + util::ToUnderlying(ThreadState_SuspendShift))); /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } + this->UpdateState(); } /* Update our SVC access permissions. */ @@ -476,26 +477,23 @@ namespace ams::kern { } KScheduler::OnThreadAffinityMaskChanged(this, old_mask, active_core); } + + m_base_priority = m_base_priority_on_unpin; + RestorePriority(this); } /* Allow performing thread suspension (if termination hasn't been requested). */ - { + if (!this->IsTerminationRequested()) { /* Update our allow flags. */ - if (!this->IsTerminationRequested()) { - m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift)); - } + m_suspend_allowed_flags |= (1 << (util::ToUnderlying(SuspendType_Thread) + util::ToUnderlying(ThreadState_SuspendShift))); /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } - } + this->UpdateState(); - /* Update our SVC access permissions. */ - MESOSPHERE_ASSERT(m_parent != nullptr); - m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + /* Update our SVC access permissions. */ + MESOSPHERE_ASSERT(m_parent != nullptr); + m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + } /* Resume any threads that began waiting on us while we were pinned. */ for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); ++it) { @@ -599,14 +597,16 @@ namespace ams::kern { KScopedSchedulerLock sl; MESOSPHERE_ASSERT(m_num_core_migration_disables >= 0); - /* If the core id is no-update magic, preserve the ideal core id. */ - if (core_id == ams::svc::IdealCoreNoUpdate) { + /* If we're updating, set our ideal virtual core. */ + if (core_id != ams::svc::IdealCoreNoUpdate) { + m_virtual_ideal_core_id = core_id; + } else { + /* Preserve our ideal core id. */ core_id = m_virtual_ideal_core_id; R_UNLESS(((1ul << core_id) & v_affinity_mask) != 0, svc::ResultInvalidCombination()); } - /* Set the virtual core/affinity mask. */ - m_virtual_ideal_core_id = core_id; + /* Set our affinity mask. */ m_virtual_affinity_mask = v_affinity_mask; /* Translate the virtual core to a physical core. */ @@ -708,13 +708,38 @@ namespace ams::kern { KScopedSchedulerLock sl; + /* Determine the priority value to use. */ + const s32 target_priority = m_termination_requested.load() && priority >= TerminatingThreadPriority ? TerminatingThreadPriority : priority; + /* Change our base priority. */ - m_base_priority = priority; + if (this->GetStackParameters().is_pinned) { + m_base_priority_on_unpin = target_priority; + } else { + m_base_priority = target_priority; + } /* Perform a priority restoration. */ RestorePriority(this); } + void KThread::IncreaseBasePriority(s32 priority) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority); + + /* Set our unpin base priority, if we're pinned. */ + if (this->GetStackParameters().is_pinned && m_base_priority_on_unpin > priority) { + m_base_priority_on_unpin = priority; + } + + /* Set our base priority. */ + if (m_base_priority > priority) { + m_base_priority = priority; + + /* Perform a priority restoration. */ + RestorePriority(this); + } + } + Result KThread::SetPriorityToIdle() { MESOSPHERE_ASSERT_THIS(); @@ -735,7 +760,7 @@ namespace ams::kern { KScopedSchedulerLock lk; /* Note the request in our flags. */ - m_suspend_request_flags |= (1u << (ThreadState_SuspendShift + type)); + m_suspend_request_flags |= (1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type))); /* Try to perform the suspend. */ this->TrySuspend(); @@ -747,14 +772,10 @@ namespace ams::kern { KScopedSchedulerLock sl; /* Clear the request in our flags. */ - m_suspend_request_flags &= ~(1u << (ThreadState_SuspendShift + type)); + m_suspend_request_flags &= ~(1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type))); /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } + this->UpdateState(); } void KThread::WaitCancel() { @@ -790,20 +811,22 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(this->GetNumKernelWaiters() == 0); /* Perform the suspend. */ - this->Suspend(); + this->UpdateState(); } - void KThread::Suspend() { + void KThread::UpdateState() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); - MESOSPHERE_ASSERT(this->IsSuspendRequested()); /* Set our suspend flags in state. */ const auto old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); + const auto new_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); + m_thread_state = new_state; /* Note the state change in scheduler. */ - KScheduler::OnThreadStateChanged(this, old_state); + if (new_state != old_state) { + KScheduler::OnThreadStateChanged(this, old_state); + } } void KThread::Continue() { @@ -956,6 +979,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(); } /* Insert the waiter. */ @@ -970,6 +994,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(); } /* Remove the waiter. */ @@ -1048,6 +1073,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(); } it = m_waiter_list.erase(it); @@ -1094,15 +1120,21 @@ namespace ams::kern { /* If the current thread has been asked to suspend, suspend it and retry. */ if (GetCurrentThread().IsSuspended()) { - GetCurrentThread().Suspend(); + GetCurrentThread().UpdateState(); continue; } /* If we're not a kernel thread and we've been asked to suspend, suspend ourselves. */ - if (this->IsUserThread() && this->IsSuspended()) { - this->Suspend(); + if (KProcess *parent = this->GetOwnerProcess(); parent != nullptr) { + if (this->IsSuspended()) { + this->UpdateState(); + } + parent->IncrementRunningThreadCount(); } + /* Open a reference, now that we're running. */ + this->Open(); + /* Set our state and finish. */ this->SetState(KThread::ThreadState_Runnable); return ResultSuccess(); @@ -1117,18 +1149,23 @@ namespace ams::kern { /* Call the debug callback. */ KDebug::OnExitThread(this); - /* Release the thread resource hint from parent. */ + /* Release the thread resource hint, running thread count from parent. */ if (m_parent != nullptr) { m_parent->ReleaseResource(ams::svc::LimitableResource_ThreadCountMax, 0, 1); m_resource_limit_release_hint = true; + m_parent->DecrementRunningThreadCount(); } + /* Destroy any dependent objects. */ + this->DestroyClosedObjects(); + /* Perform termination. */ { KScopedSchedulerLock sl; /* Disallow all suspension. */ m_suspend_allowed_flags = 0; + this->UpdateState(); /* Start termination. */ this->StartTermination(); @@ -1140,7 +1177,7 @@ namespace ams::kern { MESOSPHERE_PANIC("KThread::Exit() would return"); } - void KThread::Terminate() { + Result KThread::Terminate() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(this != GetCurrentThreadPointer()); @@ -1149,7 +1186,9 @@ namespace ams::kern { /* If the thread isn't terminated, wait for it to terminate. */ s32 index; KSynchronizationObject *objects[] = { this }; - KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite); + return KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite); + } else { + return ResultSuccess(); } } @@ -1177,15 +1216,20 @@ namespace ams::kern { /* Register the terminating dpc. */ this->RegisterDpc(DpcFlag_Terminating); + /* If the thread is pinned, unpin it. */ + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } + /* If the thread is suspended, continue it. */ if (this->IsSuspended()) { m_suspend_allowed_flags = 0; - this->Continue(); + this->UpdateState(); } /* Change the thread's priority to be higher than any system thread's. */ if (this->GetBasePriority() >= ams::svc::SystemThreadPriorityHighest) { - this->SetBasePriority(ams::svc::SystemThreadPriorityHighest - 1); + this->SetBasePriority(TerminatingThreadPriority); } /* If the thread is runnable, send a termination interrupt to other cores. */ diff --git a/libraries/libmesosphere/source/kern_k_transfer_memory.cpp b/libraries/libmesosphere/source/kern_k_transfer_memory.cpp index 283383a87..0abf544f5 100644 --- a/libraries/libmesosphere/source/kern_k_transfer_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_transfer_memory.cpp @@ -23,12 +23,11 @@ namespace ams::kern { /* Set members. */ m_owner = GetCurrentProcessPointer(); - /* Initialize the page group. */ + /* Get the owner page table. */ auto &page_table = m_owner->GetPageTable(); - new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager()); - /* Ensure that our page group's state is valid on exit. */ - auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); }; + /* Construct the page group, guarding to make sure our state is valid on exit. */ + auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager()); /* Lock the memory. */ R_TRY(page_table.LockForTransferMemory(GetPointer(m_page_group), addr, size, ConvertToKMemoryPermission(own_perm))); diff --git a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp index f2ddc9953..f525acd28 100644 --- a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp @@ -68,6 +68,9 @@ namespace ams::kern { /* Do the task. */ task->DoWorkerTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 5ee230a41..dc351c708 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -49,6 +49,9 @@ namespace ams::kern { /* Initialize the carveout and the system resource limit. */ KSystemControl::InitializePhase1(); + /* Synchronize all cores before proceeding, to ensure access to the global rng is consistent. */ + cpu::SynchronizeAllCores(); + /* Initialize the memory manager and the KPageBuffer slabheap. */ { const auto &management_region = KMemoryLayout::GetPoolManagementRegion(); @@ -74,6 +77,9 @@ namespace ams::kern { Kernel::InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize()); } + } else { + /* Synchronize all cores before proceeding, to ensure access to the global rng is consistent. */ + cpu::SynchronizeAllCores(); } /* Initialize the supervisor page table for each core. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp index 8deb3f595..e5b6e3ae4 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp @@ -30,32 +30,24 @@ namespace ams::kern::svc { /* Determine aligned extents. */ const uintptr_t aligned_start = util::AlignDown(address, PageSize); const uintptr_t aligned_end = util::AlignUp(address + size, PageSize); - const size_t num_pages = (aligned_end - aligned_start) / PageSize; - /* Create a page group for the process's memory. */ - KPageGroup pg(page_table.GetBlockInfoManager()); - - /* Make and open the page group. */ - R_TRY(page_table.MakeAndOpenPageGroup(std::addressof(pg), - aligned_start, num_pages, - KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, - KMemoryPermission_UserRead, KMemoryPermission_UserRead, - KMemoryAttribute_Uncached, KMemoryAttribute_None)); - - /* Ensure we don't leak references to the pages we're operating on. */ - ON_SCOPE_EXIT { pg.Close(); }; - - /* Operate on all the blocks. */ + /* Iterate over and operate on contiguous ranges. */ uintptr_t cur_address = aligned_start; size_t remaining = size; - for (const auto &block : pg) { - /* Get the block extents. */ - KVirtualAddress operate_address = block.GetAddress(); - size_t operate_size = block.GetSize(); + while (remaining > 0) { + /* Get a contiguous range to operate on. */ + KPageTableBase::MemoryRange contig_range = {}; + R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address)); + + /* Close the range when we're done operating on it. */ + ON_SCOPE_EXIT { contig_range.Close(); }; /* Adjust to remain within range. */ + KVirtualAddress operate_address = contig_range.address; + size_t operate_size = contig_range.size; if (cur_address < address) { operate_address += (address - cur_address); + operate_size -= (address - cur_address); } if (operate_size > remaining) { operate_size = remaining; @@ -65,7 +57,7 @@ namespace ams::kern::svc { operation.Operate(GetVoidPointer(operate_address), operate_size); /* Advance. */ - cur_address += block.GetSize(); + cur_address += contig_range.size; remaining -= operate_size; } MESOSPHERE_ASSERT(remaining == 0); diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 60a72fbc0..308871fa0 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -276,6 +276,25 @@ namespace ams::kern::svc { } } break; + case ams::svc::InfoType_MesosphereCurrentProcess: + { + /* Verify the input handle is invalid. */ + R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle()); + + /* Verify the sub-type is valid. */ + R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination()); + + /* Get the handle table. */ + KHandleTable &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Get a new handle for the current process. */ + ams::svc::Handle tmp; + R_TRY(handle_table.Add(std::addressof(tmp), GetCurrentProcessPointer())); + + /* Set the output. */ + *out = tmp; + } + break; default: { /* For debug, log the invalid info call. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp index 7454237bc..673dfe702 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp @@ -74,12 +74,17 @@ namespace ams::kern::svc { /* Wait for a message. */ while (true) { + /* Close any pending objects before we wait. */ + GetCurrentThread().DestroyClosedObjects(); + + /* Wait for an object. */ s32 index; Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout); if (svc::ResultTimedOut::Includes(result)) { return result; } + /* Receive the request. */ if (R_SUCCEEDED(result)) { KServerSession *session = objs[index]->DynamicCast(); if (session != nullptr) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_port.cpp b/libraries/libmesosphere/source/svc/kern_svc_port.cpp index 1d1171889..1e94b552a 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_port.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_port.cpp @@ -38,28 +38,27 @@ namespace ams::kern::svc { KPort *port = KPort::Create(); R_UNLESS(port != nullptr, svc::ResultOutOfResource()); - /* Reserve a handle for the server port. */ - R_TRY(handle_table.Reserve(out_server_handle)); - auto reserve_guard = SCOPE_GUARD { handle_table.Unreserve(*out_server_handle); }; - /* Initialize the new port. */ port->Initialize(max_sessions, false, 0); /* Register the port. */ KPort::Register(port); + /* Ensure that our only reference to the port is in the handle table when we're done. */ + ON_SCOPE_EXIT { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + }; + /* Register the handle in the table. */ - handle_table.Register(*out_server_handle, std::addressof(port->GetServerPort())); - reserve_guard.Cancel(); - auto register_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); }; + R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); + auto handle_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); }; /* Create a new object name. */ R_TRY(KObjectName::NewFromName(std::addressof(port->GetClientPort()), name)); - /* Perform resource cleanup. */ - port->GetServerPort().Close(); - port->GetClientPort().Close(); - register_guard.Cancel(); + /* We succeeded, so don't leak the handle. */ + handle_guard.Cancel(); } else /* if (max_sessions == 0) */ { /* Ensure that this else case is correct. */ MESOSPHERE_AUDIT(max_sessions == 0); @@ -157,21 +156,18 @@ namespace ams::kern::svc { R_TRY(handle_table.Reserve(out)); auto handle_guard = SCOPE_GUARD { handle_table.Unreserve(*out); }; - /* Create and register session. */ + /* Create the session. */ + KAutoObject *session; if (client_port->IsLight()) { - KLightClientSession *session; - R_TRY(client_port->CreateLightSession(std::addressof(session))); - - handle_table.Register(*out, session); - session->Close(); + R_TRY(client_port->CreateLightSession(reinterpret_cast(std::addressof(session)))); } else { - KClientSession *session; - R_TRY(client_port->CreateSession(std::addressof(session))); - - handle_table.Register(*out, session); - session->Close(); + R_TRY(client_port->CreateSession(reinterpret_cast(std::addressof(session)))); } + /* Register the session. */ + handle_table.Register(*out, session); + session->Close(); + /* We succeeded. */ handle_guard.Cancel(); return ResultSuccess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index 7625cbf3b..6d12f2bfd 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -286,12 +286,7 @@ namespace ams::kern::svc { process->SetIdealCoreId(core_id); /* Run the process. */ - R_TRY(process->Run(priority, static_cast(main_thread_stack_size))); - - /* Open a reference to the process, since it's now running. */ - process->Open(); - - return ResultSuccess(); + return process->Run(priority, static_cast(main_thread_stack_size)); } Result TerminateProcess(ams::svc::Handle process_handle) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp index bd8f8cb0c..7df608e67 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp @@ -122,21 +122,8 @@ namespace ams::kern::svc { R_UNLESS(src_pt.Contains(src_address, size), svc::ResultInvalidCurrentMemory()); R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState_SharedCode), svc::ResultInvalidMemoryRegion()); - /* Create a new page group. */ - KPageGroup pg(dst_pt.GetBlockInfoManager()); - - /* Make the page group. */ - R_TRY(src_pt.MakeAndOpenPageGroup(std::addressof(pg), - src_address, size / PageSize, - KMemoryState_FlagCanMapProcess, KMemoryState_FlagCanMapProcess, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_All, KMemoryAttribute_None)); - - /* Close the page group when we're done. */ - ON_SCOPE_EXIT { pg.Close(); }; - - /* Unmap the group. */ - R_TRY(dst_pt.UnmapPageGroup(dst_address, pg, KMemoryState_SharedCode)); + /* Unmap the memory. */ + R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); return ResultSuccess(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_session.cpp b/libraries/libmesosphere/source/svc/kern_svc_session.cpp index dc4e54dc3..f55eea5a1 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_session.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_session.cpp @@ -43,8 +43,8 @@ namespace ams::kern::svc { /* Ensure that we clean up the session (and its only references are handle table) on function end. */ ON_SCOPE_EXIT { - session->GetServerSession().Close(); session->GetClientSession().Close(); + session->GetServerSession().Close(); }; /* Register the session. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index c887c5f56..e8277a22b 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -75,11 +75,7 @@ namespace ams::kern::svc { R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); /* Try to start the thread. */ - R_TRY(thread->Run()); - - /* If we succeeded, persist a reference to the thread. */ - thread->Open(); - return ResultSuccess(); + return thread->Run(); } void ExitThread() { @@ -132,14 +128,17 @@ namespace ams::kern::svc { /* Get the current process. */ KProcess &process = GetCurrentProcess(); - /* Validate the priority. */ - R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority()); - R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority()); - /* Get the thread from its handle. */ KScopedAutoObject thread = process.GetHandleTable().GetObject(thread_handle); R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); + /* Validate the thread is owned by the current process. */ + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle()); + + /* Validate the priority. */ + R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority()); + R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority()); + /* Set the thread priority. */ thread->SetBasePriority(priority); return ResultSuccess(); @@ -157,6 +156,13 @@ namespace ams::kern::svc { } Result SetThreadCoreMask(ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) { + /* Get the thread from its handle. */ + KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); + + /* Validate the thread is owned by the current process. */ + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle()); + /* Determine the core id/affinity mask. */ if (core_id == ams::svc::IdealCoreUseProcessValue) { core_id = GetCurrentProcess().GetIdealCoreId(); @@ -175,10 +181,6 @@ namespace ams::kern::svc { } } - /* Get the thread from its handle. */ - KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); - R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); - /* Set the core mask. */ R_TRY(thread->SetCoreMask(core_id, affinity_mask)); diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile index 37bac54f2..8c3fbc990 100644 --- a/libraries/libstratosphere/Makefile +++ b/libraries/libstratosphere/Makefile @@ -21,9 +21,9 @@ PRECOMPILED_HEADERS := $(CURRENT_DIRECTORY)/include/stratosphere.hpp #PRECOMPILED_HEADERS := DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE -SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 +SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -flto CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) -CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) -Wl,-Map,$(notdir $*.map) @@ -77,7 +77,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I. -.PHONY: clean all +.PHONY: clean all lib/$(TARGET).a #--------------------------------------------------------------------------------- all: lib/$(TARGET).a @@ -124,6 +124,7 @@ $(OFILES_SRC) : $(HFILES_BIN) ams_environment_weak.o: CXXFLAGS += -fno-lto pm_info_api_weak.o: CXXFLAGS += -fno-lto +hos_stratosphere_api.o: CXXFLAGS += -fno-lto #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index afa38728c..5399f1978 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -75,15 +80,22 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include +#include #include /* Include FS last. */ #include #include -#include \ No newline at end of file +#include + +/* External modules that we're including. */ +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp index c237ed05f..13f20a5d5 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp @@ -29,4 +29,8 @@ namespace ams { void *Malloc(size_t size); void Free(void *ptr); + void *MallocForRapidJson(size_t size); + void *ReallocForRapidJson(void *ptr, size_t size); + void FreeForRapidJson(void *ptr); + } diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index 872aa7623..88d7a45bb 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -129,6 +129,46 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(10, lm, Flush); AMS_DEFINE_SYSTEM_THREAD(10, lm, HtcsConnection); + /* htc. */ + AMS_DEFINE_SYSTEM_THREAD(10, htc, Main); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcsIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcsMonitor); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcfsIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcfsMonitor); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowDiscovery); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowTcpServer); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowUsbIndication); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowListen); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowObserver); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowSend); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowReceive); + AMS_DEFINE_SYSTEM_THREAD(10, htc, Htcmisc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscReceive); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscSend); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcObserver); + + AMS_DEFINE_SYSTEM_THREAD(10, tma, BridgePcieDriver); + + /* cs/scs. */ + AMS_DEFINE_SYSTEM_THREAD(20, cs, Main); + AMS_DEFINE_SYSTEM_THREAD(20, cs, HidctlService); + AMS_DEFINE_SYSTEM_THREAD(20, cs, HidctlLegacyServer); + AMS_DEFINE_SYSTEM_THREAD(20, cs, AudioServer); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcVideoSender); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcVideoReader); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcAudioSender); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcAudioReader); + + AMS_DEFINE_SYSTEM_THREAD(21, scs, ShellServer); + AMS_DEFINE_SYSTEM_THREAD(21, scs, ShellEventHandler); + + /* DevServer/TioServer. */ + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, Main); + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, FileServerHtcsServer); + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, SdCardObserver); + + #undef AMS_DEFINE_SYSTEM_THREAD } diff --git a/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp b/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp index 5cb515619..3d7b13b27 100644 --- a/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp @@ -40,6 +40,7 @@ namespace ams::cfg { bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag); bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag); bool HasGlobalFlag(const char *flag); + Result DeleteGlobalFlag(const char *flag); /* HBL Configuration utilities. */ bool HasHblFlag(const char *flag); diff --git a/libraries/libstratosphere/include/stratosphere/cs.hpp b/libraries/libstratosphere/include/stratosphere/cs.hpp new file mode 100644 index 000000000..9e2c0cbee --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include + +#include diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp new file mode 100644 index 000000000..ccb5088d4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeAudioServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp new file mode 100644 index 000000000..22912c523 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::cs { + + using CommandHeader = scs::CommandHeader; + using ResponseHeader = scs::ResponseHeader; + + class CommandProcessor : public scs::CommandProcessor { + public: + virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp new file mode 100644 index 000000000..4d2c95070 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeHidServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp new file mode 100644 index 000000000..a38ce23de --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeRemoteVideoServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp new file mode 100644 index 000000000..a163b661c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeTargetIoServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp index 467708841..2c921903c 100644 --- a/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp @@ -20,14 +20,7 @@ namespace ams::dd { using ProcessHandle = ::Handle; - /* TODO gcc-11: using MemoryPermission = os::MemoryPermission; using enum os::MemoryPermission; */ - - enum MemoryPermission { - MemoryPermission_None = 0, - MemoryPermission_ReadOnly = (1u << 0), - MemoryPermission_WriteOnly = (1u << 1), - - MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly, - }; + using MemoryPermission = os::MemoryPermission; + using enum os::MemoryPermission; } diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp index 376601bbb..e1d74fbc3 100644 --- a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp @@ -49,7 +49,7 @@ namespace ams::ddsf { NON_MOVEABLE(DeviceCodeEntryHolder); private: util::IntrusiveListNode list_node; - TYPED_STORAGE(DeviceCodeEntry) entry_storage; + util::TypedStorage entry_storage; bool is_constructed; public: using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&DeviceCodeEntryHolder::list_node>; @@ -80,7 +80,7 @@ namespace ams::ddsf { DeviceCodeEntry &Construct(DeviceCode dc, IDevice *dev) { AMS_ASSERT(!this->IsConstructed()); - DeviceCodeEntry *entry = new (GetPointer(this->entry_storage)) DeviceCodeEntry(dc, dev); + DeviceCodeEntry *entry = util::ConstructAt(this->entry_storage, dc, dev); this->is_constructed = true; return *entry; } @@ -91,7 +91,7 @@ namespace ams::ddsf { void Destroy() { AMS_ASSERT(this->IsConstructed()); - GetReference(this->entry_storage).~DeviceCodeEntry(); + util::DestroyAt(this->entry_storage); this->is_constructed = false; } diff --git a/libraries/libstratosphere/include/stratosphere/erpt/erpt_ids.autogen.hpp b/libraries/libstratosphere/include/stratosphere/erpt/erpt_ids.autogen.hpp index 601e1f93e..7d6bf5444 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/erpt_ids.autogen.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/erpt_ids.autogen.hpp @@ -168,6 +168,9 @@ HANDLER(ResourceLimitLimitInfo, 127) \ HANDLER(ResourceLimitPeakInfo, 128) \ HANDLER(TouchScreenInfo, 129) \ + HANDLER(AcpUserAccountSettingsInfo, 130) \ + HANDLER(AudioDeviceInfo, 131) \ + HANDLER(AbnormalWakeInfo, 132) \ #define AMS_ERPT_FOREACH_FIELD(HANDLER) \ HANDLER(TestU64, 0, Test, FieldType_NumericU64, FieldFlag_None ) \ @@ -658,7 +661,7 @@ HANDLER(LcsApplicationRequestFlag, 485, ErrorInfo, FieldType_Bool, FieldFlag_None ) \ HANDLER(LcsHasExFatDriverFlag, 486, ErrorInfo, FieldType_Bool, FieldFlag_None ) \ HANDLER(LcsIpAddress, 487, ErrorInfo, FieldType_NumericU32, FieldFlag_None ) \ - HANDLER(AcpStartupUserAccount, 488, AcpGeneralSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AcpStartupUserAccount, 488, AcpUserAccountSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(AcpAocRegistrationType, 489, AcpAocSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(AcpAttributeFlag, 490, AcpGeneralSettingsInfo, FieldType_NumericU32, FieldFlag_None ) \ HANDLER(AcpSupportedLanguageFlag, 491, AcpGeneralSettingsInfo, FieldType_NumericU32, FieldFlag_None ) \ @@ -712,7 +715,7 @@ HANDLER(EncryptedDyingMessage, 539, ErrorInfo, FieldType_U8Array, FieldFlag_None ) \ HANDLER(DramId, 540, PowerClockInfo, FieldType_NumericU32, FieldFlag_None ) \ HANDLER(NifmConnectionTestRedirectUrl, 541, NifmConnectionTestInfo, FieldType_String, FieldFlag_None ) \ - HANDLER(AcpRequiredNetworkServiceLicenseOnLaunchFlag, 542, AcpGeneralSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AcpRequiredNetworkServiceLicenseOnLaunchFlag, 542, AcpUserAccountSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(PciePort0Flags, 543, PcieLoggedStateInfo, FieldType_NumericU32, FieldFlag_None ) \ HANDLER(PciePort0Speed, 544, PcieLoggedStateInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(PciePort0ResetTimeInUs, 545, PcieLoggedStateInfo, FieldType_NumericU32, FieldFlag_None ) \ @@ -743,12 +746,12 @@ HANDLER(ApplicationInFocusTime, 570, ErrorInfoAuto, FieldType_NumericI64, FieldFlag_None ) \ HANDLER(ApplicationOutOfFocusTime, 571, ErrorInfoAuto, FieldType_NumericI64, FieldFlag_None ) \ HANDLER(ApplicationBackgroundFocusTime, 572, ErrorInfoAuto, FieldType_NumericI64, FieldFlag_None ) \ - HANDLER(AcpUserAccountSwitchLock, 573, AcpGeneralSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AcpUserAccountSwitchLock, 573, AcpUserAccountSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(USB3HostAvailableFlag, 574, USB3AvailableInfo, FieldType_Bool, FieldFlag_None ) \ HANDLER(USB3DeviceAvailableFlag, 575, USB3AvailableInfo, FieldType_Bool, FieldFlag_None ) \ HANDLER(AcpNeighborDetectionClientConfigurationSendDataId, 576, AcpNeighborDetectionInfo, FieldType_NumericU64, FieldFlag_None ) \ HANDLER(AcpNeighborDetectionClientConfigurationReceivableDataIds, 577, AcpNeighborDetectionInfo, FieldType_U64Array, FieldFlag_None ) \ - HANDLER(AcpStartupUserAccountOptionFlag, 578, AcpGeneralSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AcpStartupUserAccountOptionFlag, 578, AcpUserAccountSettingsInfo, FieldType_NumericU8, FieldFlag_None ) \ HANDLER(ServerErrorCode, 579, ErrorInfo, FieldType_String, FieldFlag_None ) \ HANDLER(AppletManagerMetaLogTrace, 580, ErrorInfo, FieldType_U64Array, FieldFlag_None ) \ HANDLER(ServerCertificateSerialNumber, 581, NetworkSecurityCertificateInfo, FieldType_String, FieldFlag_None ) \ @@ -800,4 +803,13 @@ HANDLER(SystemSessionCountPeak, 627, ResourceLimitPeakInfo, FieldType_NumericI64, FieldFlag_None ) \ HANDLER(GpuCrashHash, 628, GpuCrashInfo, FieldType_U8Array, FieldFlag_None ) \ HANDLER(TouchScreenPanelGpioValue, 629, TouchScreenInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(BrowserCertificateHostName, 630, ErrorInfo, FieldType_String, FieldFlag_None ) \ + HANDLER(BrowserCertificateCommonName, 631, ErrorInfo, FieldType_String, FieldFlag_None ) \ + HANDLER(BrowserCertificateOrganizationalUnitName, 632, ErrorInfo, FieldType_String, FieldFlag_None ) \ + HANDLER(FsPooledBufferFailedIdealAllocationCountOnAsyncAccess, 633, FsMemoryInfo, FieldType_NumericU64, FieldFlag_None ) \ + HANDLER(AudioOutputTarget, 634, AudioDeviceInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AudioOutputChannelCount, 635, AudioDeviceInfo, FieldType_NumericU8, FieldFlag_None ) \ + HANDLER(AppletTotalActiveTime, 636, ErrorInfoAuto, FieldType_NumericI64, FieldFlag_None ) \ + HANDLER(WakeCount, 637, AbnormalWakeInfo, FieldType_NumericU32, FieldFlag_None ) \ + HANDLER(PredominantWakeReason, 638, AbnormalWakeInfo, FieldType_NumericU32, FieldFlag_None ) \ diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp index 91187c031..ce4ede8cb 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp @@ -26,14 +26,18 @@ AMS_SF_METHOD_INFO(C, H, 2, Result, SetInitialLaunchSettingsCompletionTime, (const time::SteadyClockTimePoint &time_point), (time_point), hos::Version_3_0_0) \ AMS_SF_METHOD_INFO(C, H, 3, Result, ClearInitialLaunchSettingsCompletionTime, (), (), hos::Version_3_0_0) \ AMS_SF_METHOD_INFO(C, H, 4, Result, UpdatePowerOnTime, (), (), hos::Version_3_0_0) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0, hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0, hos::Version_12_0_0) \ AMS_SF_METHOD_INFO(C, H, 7, Result, UpdateApplicationLaunchTime, (), (), hos::Version_6_0_0) \ AMS_SF_METHOD_INFO(C, H, 8, Result, ClearApplicationLaunchTime, (), (), hos::Version_6_0_0) \ AMS_SF_METHOD_INFO(C, H, 9, Result, SubmitAttachment, (ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_8_0_0) \ AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachmentsDeprecated, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer), hos::Version_8_0_0, hos::Version_10_2_0) \ AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachments, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer, result), hos::Version_11_0_0) \ - AMS_SF_METHOD_INFO(C, H, 11, Result, CreateReport, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result), (report_type, ctx_buffer, str_buffer, meta_buffer, result), hos::Version_11_0_0) + AMS_SF_METHOD_INFO(C, H, 11, Result, CreateReport, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result), (report_type, ctx_buffer, str_buffer, meta_buffer, result), hos::Version_11_0_0) \ + AMS_SF_METHOD_INFO(C, H, 20, Result, RegisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 21, Result, UnregisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 22, Result, UpdateAppletSuspendedDuration, (ncm::ProgramId program_id, TimeSpanType duration), (program_id, duration), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 30, Result, InvalidateForcedShutdownDetection, (), (), hos::Version_12_0_0) AMS_SF_DEFINE_INTERFACE(ams::erpt::sf, IContext, AMS_ERPT_I_CONTEXT_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp index b5eaa84e2..e42d3b986 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp @@ -22,8 +22,9 @@ namespace ams::erpt::srv { constexpr inline const char ReportOnSdStoragePath[] = "ersd"; - constexpr inline const char ReportStoragePath[] = "save"; - constexpr inline const char JournalFileName[] = "save:/journal"; + constexpr inline const char ReportStoragePath[] = "save"; + constexpr inline const char JournalFileName[] = "save:/journal"; + constexpr inline const char ForcedShutdownContextFileName[] = "save:/forced-shutdown"; constexpr size_t ReportFileNameLength = 64; constexpr size_t AttachmentFileNameLength = 64; diff --git a/libraries/libstratosphere/include/stratosphere/err.hpp b/libraries/libstratosphere/include/stratosphere/err.hpp index c8cb979a0..f8dd9fc6b 100644 --- a/libraries/libstratosphere/include/stratosphere/err.hpp +++ b/libraries/libstratosphere/include/stratosphere/err.hpp @@ -16,4 +16,6 @@ #pragma once +#include #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp b/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp new file mode 100644 index 000000000..db5c85b32 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/err/err_system_api.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::err { + + ErrorCode ConvertResultToErrorCode(const Result &result); + void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code); + +} diff --git a/libraries/libstratosphere/include/stratosphere/err/err_types.hpp b/libraries/libstratosphere/include/stratosphere/err/err_types.hpp new file mode 100644 index 000000000..9b785bac1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/err/err_types.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::err { + + using ErrorCodeCategory = u32; + using ErrorCodeNumber = u32; + + struct ErrorCode { + static constexpr auto StringLengthMax = 15; + + ErrorCodeCategory category; + ErrorCodeNumber number; + + constexpr ALWAYS_INLINE bool IsValid() const { return this->category > 0; } + }; + + constexpr inline ErrorCode InvalidErrorCode = { + .category = 0, + .number = 0, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 4428ac11c..3b81c8af3 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp new file mode 100644 index 000000000..375ed1afd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + class IEventNotifier { + public: + virtual ~IEventNotifier() { /* ... */ } + + Result BindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) { + AMS_ASSERT(out != nullptr); + return this->DoBindEvent(out, clear_mode); + } + private: + virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp index 9cc59e790..4049e7f93 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp @@ -68,7 +68,7 @@ namespace ams::fs { virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { switch (op_id) { - case OperationId::InvalidateCache: + case OperationId::Invalidate: return ResultSuccess(); case OperationId::QueryRange: R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp index bd3bb06ca..3d2b8a285 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp @@ -18,11 +18,16 @@ namespace ams::fs { - enum class OperationId : u64 { - Clear = ::FsOperationId_Clear, - ClearSignature = ::FsOperationId_ClearSignature, - InvalidateCache = ::FsOperationId_InvalidateCache, - QueryRange = ::FsOperationId_QueryRange, + enum class OperationId : s64 { + FillZero = 0, + DestroySignature = 1, + Invalidate = 2, + QueryRange = 3, + QueryUnpreparedRange = 4, + QueryLazyLoadCompletionRate = 5, + SetLazyLoadPriority = 6, + + ReadLazyLoadFileForciblyForDebug = 10001, }; } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp index 9e70d3d8a..f3771423f 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp @@ -55,7 +55,7 @@ namespace ams::fs { virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { switch (op_id) { - case OperationId::InvalidateCache: + case OperationId::Invalidate: case OperationId::QueryRange: return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); default: diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp index 38690d34f..be6e0f05b 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -164,6 +164,6 @@ namespace ams::fs { static_assert(util::is_pod::value); static_assert(sizeof(HashSalt) == HashSalt::Size); - using SaveDataHashSalt = std::optional; + using SaveDataHashSalt = util::optional; } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp index 84c6c2242..183f7bea5 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp @@ -18,28 +18,14 @@ namespace ams::fs { - class SdCardDetectionEventNotifier { - NON_COPYABLE(SdCardDetectionEventNotifier); - NON_MOVEABLE(SdCardDetectionEventNotifier); - private: - void *notifier; - public: - SdCardDetectionEventNotifier() : notifier(nullptr) {} - ~SdCardDetectionEventNotifier(); - - void Open(void *notifier) { - this->notifier = notifier; - } - - Result GetEventHandle(os::SystemEvent *out_event, os::EventClearMode clear_mode); - }; + class IEventNotifier; Result MountSdCard(const char *name); Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name); + Result OpenSdCardDetectionEventNotifier(std::unique_ptr *out); + bool IsSdCardInserted(); - Result OpenSdCardDetectionEventNotifier(SdCardDetectionEventNotifier *out); - } diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp index c0bf8d001..ac7bc439e 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp @@ -20,23 +20,23 @@ namespace ams::fs::impl { class Newable { public: - static void *operator new(size_t size) { + static ALWAYS_INLINE void *operator new(size_t size) noexcept { return ::ams::fs::impl::Allocate(size); } - static void *operator new(size_t size, Newable *placement) { + static ALWAYS_INLINE void *operator new(size_t size, Newable *placement) noexcept { return placement; } - static void *operator new[](size_t size) { + static ALWAYS_INLINE void *operator new[](size_t size) noexcept { return ::ams::fs::impl::Allocate(size); } - static void operator delete(void *ptr, size_t size) { + static ALWAYS_INLINE void operator delete(void *ptr, size_t size) noexcept { return ::ams::fs::impl::Deallocate(ptr, size); } - static void operator delete[](void *ptr, size_t size) { + static ALWAYS_INLINE void operator delete[](void *ptr, size_t size) noexcept { return ::ams::fs::impl::Deallocate(ptr, size); } }; diff --git a/libraries/libstratosphere/include/stratosphere/fssrv.hpp b/libraries/libstratosphere/include/stratosphere/fssrv.hpp index a2fb95b3d..8054a3d23 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv.hpp @@ -17,6 +17,8 @@ #pragma once #include #include +#include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/impl/fssrv_impl_program_index_map_info_manager.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/impl/fssrv_impl_program_index_map_info_manager.hpp new file mode 100644 index 000000000..a0db001d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/impl/fssrv_impl_program_index_map_info_manager.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::fssrv::impl { + + struct ProgramIndexMapInfoEntry : public ::ams::util::IntrusiveListBaseNode, public ::ams::fs::impl::Newable { + ncm::ProgramId program_id; + ncm::ProgramId base_program_id; + u8 program_index; + }; + + class ProgramIndexMapInfoManager { + NON_COPYABLE(ProgramIndexMapInfoManager); + NON_MOVEABLE(ProgramIndexMapInfoManager); + private: + using ProgramIndexMapInfoList = util::IntrusiveListBaseTraits::ListType; + private: + ProgramIndexMapInfoList m_list; + mutable os::SdkMutex m_mutex; + public: + constexpr ProgramIndexMapInfoManager() : m_list(), m_mutex() { /* ... */ } + + void Clear() { + /* Acquire exclusive access to the map. */ + std::scoped_lock lk(m_mutex); + + /* Actually clear. */ + this->ClearImpl(); + } + + size_t GetProgramCount() const { + /* Acquire exclusive access to the map. */ + std::scoped_lock lk(m_mutex); + + /* Get the size. */ + return m_list.size(); + } + + util::optional Get(const ncm::ProgramId &program_id) const { + /* Acquire exclusive access to the map. */ + std::scoped_lock lk(m_mutex); + + /* Get the entry from the map. */ + return this->GetImpl([&] (const ProgramIndexMapInfoEntry &entry) { + return entry.program_id == program_id; + }); + } + + ncm::ProgramId GetProgramId(const ncm::ProgramId &program_id, u8 program_index) const { + /* Acquire exclusive access to the map. */ + std::scoped_lock lk(m_mutex); + + /* Get the program info for the desired program id. */ + const auto base_info = this->GetImpl([&] (const ProgramIndexMapInfoEntry &entry) { + return entry.program_id == program_id; + }); + + /* Check that an entry exists for the program id. */ + if (!base_info.has_value()) { + return ncm::InvalidProgramId; + } + + /* Get a program info which matches the same base program with the desired index. */ + const auto target_info = this->GetImpl([&] (const ProgramIndexMapInfoEntry &entry) { + return entry.base_program_id == base_info->base_program_id && entry.program_index == program_index; + }); + + /* Return the desired program id. */ + if (target_info.has_value()) { + return target_info->program_id; + } else { + return ncm::InvalidProgramId; + } + } + + Result Reset(const fs::ProgramIndexMapInfo *infos, int count) { + /* Acquire exclusive access to the map. */ + std::scoped_lock lk(m_mutex); + + /* Clear the map, and ensure we remain clear if we fail after this point. */ + this->ClearImpl(); + auto clear_guard = SCOPE_GUARD { this->ClearImpl(); }; + + /* Add each info to the list. */ + for (int i = 0; i < count; ++i) { + /* Allocate new entry. */ + auto *entry = new ProgramIndexMapInfoEntry; + R_UNLESS(entry != nullptr, fs::ResultAllocationFailureInNew()); + + /* Copy over the info. */ + entry->program_id = infos[i].program_id; + entry->base_program_id = infos[i].base_program_id; + entry->program_index = infos[i].program_index; + + /* Add to the list. */ + m_list.push_back(*entry); + } + + /* We successfully imported the map. */ + clear_guard.Cancel(); + return ResultSuccess(); + } + private: + void ClearImpl() { + /* Delete all entries. */ + while (!m_list.empty()) { + /* Get the first entry. */ + ProgramIndexMapInfoEntry *front = std::addressof(*m_list.begin()); + + /* Erase it from the list. */ + m_list.erase(m_list.iterator_to(*front)); + + /* Delete the entry. */ + delete front; + } + } + + template + util::optional GetImpl(F f) const { + /* Try to find an entry matching the predicate. */ + util::optional match = util::nullopt; + + for (const auto &entry : m_list) { + /* If the predicate matches, we want to return the relevant info. */ + if (f(entry)) { + match.emplace(); + + match->program_id = entry.program_id; + match->base_program_id = entry.base_program_id; + match->program_index = entry.program_index; + + break; + } + } + + return match; + } + }; + + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp index 8acf9c2f1..a84556fa0 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp @@ -56,6 +56,7 @@ namespace ams::fssrv::impl { Result SetSize(s64 size); Result GetSize(ams::sf::Out out); Result OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size); + Result OperateRangeWithBuffer(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 op_id, s64 offset, s64 size); }; static_assert(fssrv::sf::IsIFile); @@ -91,7 +92,7 @@ namespace ams::fssrv::impl { public: bool IsDeepRetryEnabled() const; bool IsAccessFailureDetectionObserved() const; - std::optional> AcquireCacheInvalidationReadLock(); + util::optional> AcquireCacheInvalidationReadLock(); os::ReadWriteLock &GetReadWriteLockForCacheInvalidation(); public: /* Command API. */ diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp index f3151543c..b2cb7966c 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp @@ -44,7 +44,7 @@ namespace ams::fssrv::impl { ~StorageInterfaceAdapter(); private: - std::optional> AcquireCacheInvalidationReadLock(); + util::optional> AcquireCacheInvalidationReadLock(); public: /* Command API. */ Result Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size); diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp new file mode 100644 index 000000000..090369985 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#define AMS_FSSRV_I_EVENT_NOTIFIER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetEventHandle, (ams::sf::OutCopyHandle out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IEventNotifier, AMS_FSSRV_I_EVENT_NOTIFIER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_ifile.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_ifile.hpp index a449ec32c..df5b010e6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_ifile.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_ifile.hpp @@ -19,12 +19,13 @@ #include #include -#define AMS_FSSRV_I_FILE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, Read, (ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, ams::fs::ReadOption option), (out, offset, buffer, size, option)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, Write, (s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, ams::fs::WriteOption option), (offset, buffer, size, option)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, Flush, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, SetSize, (s64 size), (size)) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, GetSize, (ams::sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, OperateRange, (ams::sf::Out out, s32 op_id, s64 offset, s64 size), (out, op_id, offset, size), hos::Version_4_0_0) +#define AMS_FSSRV_I_FILE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, Read, (ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, ams::fs::ReadOption option), (out, offset, buffer, size, option)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Write, (s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, ams::fs::WriteOption option), (offset, buffer, size, option)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, Flush, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, SetSize, (s64 size), (size)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetSize, (ams::sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, OperateRange, (ams::sf::Out out, s32 op_id, s64 offset, s64 size), (out, op_id, offset, size), hos::Version_4_0_0) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, OperateRangeWithBuffer, (const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 op_id, s64 offset, s64 size), (out_buf, in_buf, op_id, offset, size), hos::Version_12_0_0) AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IFile, AMS_FSSRV_I_FILE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp index 507fe6803..e6c37b66b 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp @@ -106,11 +106,9 @@ namespace ams::fssystem { using Newable::operator new; using Newable::operator delete; - static void *operator new(size_t, void *p) { - return p; - } - static void operator delete(void *, size_t, void*) { /* ... */ } + static ALWAYS_INLINE void *operator new(size_t, void *p) noexcept { return p; } + static ALWAYS_INLINE void operator delete(void *, size_t, void*) noexcept { /* ... */ } }; using AttrListTraits = util::IntrusiveListBaseTraits; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp index 2a31cbbae..281b4b99b 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp @@ -34,9 +34,9 @@ namespace ams::fssystem { virtual ~DirectoryRedirectionFileSystem(); protected: - inline std::optional> GetAccessorLock() const { + inline util::optional> GetAccessorLock() const { /* No accessor lock is needed. */ - return std::nullopt; + return util::nullopt; } private: Result GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp index 4a741efde..026b2d936 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp @@ -33,9 +33,9 @@ namespace ams::fssystem { virtual ~DirectorySaveDataFileSystem(); protected: - inline std::optional> GetAccessorLock() { + inline util::optional> GetAccessorLock() { /* We have a real accessor lock that we want to use. */ - return std::make_optional>(this->accessor_mutex); + return util::make_optional>(this->accessor_mutex); } private: Result AllocateWorkBuffer(std::unique_ptr *out, size_t *out_size, size_t ideal_size); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp index a45ad3d02..059f79f97 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp @@ -107,7 +107,7 @@ namespace ams::fssystem { class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore { public: using PartitionFileSystemMetaCore::Initialize; - Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional suffix = std::nullopt); + Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, util::optional suffix = util::nullopt); }; } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp index 1e5efa98e..e30e74db1 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp @@ -32,9 +32,9 @@ namespace ams::fssystem { virtual ~SubDirectoryFileSystem(); protected: - inline std::optional> GetAccessorLock() const { + inline util::optional> GetAccessorLock() const { /* No accessor lock is needed. */ - return std::nullopt; + return util::nullopt; } private: Result Initialize(const char *bp); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp index 75991c055..983d657f6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp @@ -50,7 +50,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->CreateFile(full_path, size, option); } @@ -58,7 +58,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->DeleteFile(full_path); } @@ -66,7 +66,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->CreateDirectory(full_path); } @@ -74,7 +74,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->DeleteDirectory(full_path); } @@ -82,7 +82,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->DeleteDirectoryRecursively(full_path); } @@ -92,7 +92,7 @@ namespace ams::fssystem::impl { R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->RenameFile(old_full_path, new_full_path); } @@ -102,7 +102,7 @@ namespace ams::fssystem::impl { R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->RenameDirectory(old_full_path, new_full_path); } @@ -110,7 +110,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->GetEntryType(out, full_path); } @@ -118,7 +118,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->OpenFile(out_file, full_path, mode); } @@ -126,12 +126,12 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->OpenDirectory(out_dir, full_path, mode); } virtual Result DoCommit() override { - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->Commit(); } @@ -139,7 +139,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->GetFreeSpaceSize(out, full_path); } @@ -147,7 +147,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->GetTotalSpaceSize(out, full_path); } @@ -155,7 +155,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->CleanDirectoryRecursively(full_path); } @@ -163,7 +163,7 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->GetFileTimeStampRaw(out, full_path); } @@ -171,23 +171,23 @@ namespace ams::fssystem::impl { char full_path[fs::EntryNameLengthMax + 1]; R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path); } /* These aren't accessible as commands. */ virtual Result DoCommitProvisionally(s64 counter) override { - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->CommitProvisionally(counter); } virtual Result DoRollback() override { - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->Rollback(); } virtual Result DoFlush() override { - std::optional optional_lock = static_cast(this)->GetAccessorLock(); + util::optional optional_lock = static_cast(this)->GetAccessorLock(); return this->base_fs->Flush(); } }; diff --git a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp index 7e9ce33d0..c0606b250 100644 --- a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp @@ -60,6 +60,11 @@ namespace ams::hos { Version_10_2_0 = ::ams::TargetFirmware_10_2_0, Version_11_0_0 = ::ams::TargetFirmware_11_0_0, Version_11_0_1 = ::ams::TargetFirmware_11_0_1, + Version_12_0_0 = ::ams::TargetFirmware_12_0_0, + Version_12_0_1 = ::ams::TargetFirmware_12_0_1, + Version_12_0_2 = ::ams::TargetFirmware_12_0_2, + Version_12_0_3 = ::ams::TargetFirmware_12_0_3, + Version_12_1_0 = ::ams::TargetFirmware_12_1_0, Version_Current = ::ams::TargetFirmware_Current, diff --git a/libraries/libstratosphere/include/stratosphere/htc.hpp b/libraries/libstratosphere/include/stratosphere/htc.hpp new file mode 100644 index 000000000..6a77b25f0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp new file mode 100644 index 000000000..bba165b7e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htc::server { + + constexpr inline htclow::ChannelId HtcmiscClientChannelId = 1; + constexpr inline htclow::ChannelId HtcmiscServerChannelId = 2; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp new file mode 100644 index 000000000..42f3e3e5d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class HtclowManager; + +} + +namespace ams::htc::server { + + void InitializeHtcmiscServer(htclow::HtclowManager *htclow_manager); + void FinalizeHtcmiscServer(); + void LoopHtcmiscServer(); + void RequestStopHtcmiscServer(); + + class HtcmiscImpl; + HtcmiscImpl *GetHtcmiscImpl(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp new file mode 100644 index 000000000..24825d2c5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv { + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate); + Result RegisterDefinitionFilePath(os::ProcessId process_id, const char *path, size_t size); + void UnregisterDefinitionFilePath(os::ProcessId process_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp new file mode 100644 index 000000000..dcf1742ee --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +#define AMS_HTC_TENV_I_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetVariable, (sf::Out out_size, const sf::OutBuffer &out_buffer, const htc::tenv::VariableName &name), (out_size, out_buffer, name)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetVariableLength, (sf::Out out_size,const htc::tenv::VariableName &name), (out_size, name)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, WaitUntilVariableAvailable, (s64 timeout_ms), (timeout_ms)) + +AMS_SF_DEFINE_INTERFACE(ams::htc::tenv, IService, AMS_HTC_TENV_I_SERVICE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp new file mode 100644 index 000000000..f0a11accf --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +#define AMS_HTC_TENV_I_SERVICE_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetServiceInterface, (sf::Out> out, const sf::ClientProcessId &process_id), (out, process_id)) + +AMS_SF_DEFINE_INTERFACE(ams::htc::tenv, IServiceManager, AMS_HTC_TENV_I_SERVICE_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp new file mode 100644 index 000000000..ecfae7ea9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::htc::tenv { + + constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htc:tenv"); + + class ServiceManager { + public: + Result GetServiceInterface(sf::Out> out, const sf::ClientProcessId &process_id); + }; + static_assert(htc::tenv::IsIServiceManager); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp new file mode 100644 index 000000000..fbfcaaa33 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv { + + struct VariableName { + char str[0x40]; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcfs.hpp b/libraries/libstratosphere/include/stratosphere/htcfs.hpp new file mode 100644 index 000000000..1e9368003 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcfs.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp new file mode 100644 index 000000000..eb79d1c71 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class HtclowManager; + +} + +namespace ams::htcfs { + + void Initialize(htclow::HtclowManager *htclow_manager); + + void RegisterHipcServer(); + void LoopHipcServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow.hpp b/libraries/libstratosphere/include/stratosphere/htclow.hpp new file mode 100644 index 000000000..07be9c057 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp new file mode 100644 index 000000000..cf24af32c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + using ChannelId = u16; + + struct ChannelType { + bool _is_initialized; + ModuleId _module_id; + ChannelId _channel_id; + }; + + enum ChannelState { + ChannelState_Connectable = 0, + ChannelState_Unconnectable = 1, + ChannelState_Connected = 2, + ChannelState_Disconnected = 3, + }; + + struct ChannelConfig { + bool flow_control_enabled; + bool handshake_enabled; + u64 initial_counter_max_data; + size_t max_packet_size; + }; + + constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { + switch (from) { + case ChannelState_Connectable: + return to == ChannelState_Unconnectable || + to == ChannelState_Connected || + to == ChannelState_Disconnected; + case ChannelState_Unconnectable: + return to == ChannelState_Connectable || + to == ChannelState_Disconnected; + case ChannelState_Connected: + return to == ChannelState_Disconnected; + case ChannelState_Disconnected: + return to == ChannelState_Disconnected; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp new file mode 100644 index 000000000..54ac1f924 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + class HtclowManager; + + namespace HtclowManagerHolder { + + void AddReference(); + void Release(); + + HtclowManager *GetHtclowManager(); + + void SetDefaultDriver(htclow::impl::DriverType driver_type); + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp new file mode 100644 index 000000000..3f039d153 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + enum class ModuleId : u8 { + Unknown = 0, + + Htcfs = 1, + + Htcmisc = 3, + Htcs = 4, + }; + + struct ModuleType { + bool _is_initialized; + ModuleId _id; + }; + + constexpr void InitializeModule(ModuleType *out, ModuleId id) { + *out = { + ._is_initialized = true, + ._id = id, + }; + } + + constexpr void FinalizeModule(ModuleType *out) { + *out = { + ._is_initialized = false, + ._id = ModuleId::Unknown, + }; + } + + class Module final { + private: + ModuleType m_impl; + public: + constexpr explicit Module(ModuleId id) : m_impl() { + InitializeModule(std::addressof(m_impl), id); + } + + constexpr ~Module() { + FinalizeModule(std::addressof(m_impl)); + } + + ModuleType *GetBase() { return std::addressof(m_impl); } + const ModuleType *GetBase() const { return std::addressof(m_impl); } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp new file mode 100644 index 000000000..445456e9a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + namespace impl { + + enum class DriverType { + Unknown = 0, + Debug = 1, + Socket = 2, + Usb = 3, + HostBridge = 4, + PlainChannel = 5, + }; + + } + + constexpr inline s16 ProtocolVersion = 5; + + enum ReceiveOption { + ReceiveOption_NonBlocking = 0, + ReceiveOption_ReceiveAnyData = 1, + ReceiveOption_ReceiveAllData = 2, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp new file mode 100644 index 000000000..7657e31a4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow::impl { + + struct ChannelInternalType { + ChannelId channel_id; + s8 reserved; + ModuleId module_id; + }; + static_assert(sizeof(ChannelInternalType) == 4); + + constexpr ALWAYS_INLINE ChannelInternalType ConvertChannelType(ChannelType channel) { + return { + .channel_id = channel._channel_id, + .reserved = 0, + .module_id = channel._module_id, + }; + } + + constexpr ALWAYS_INLINE bool operator==(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return lhs.module_id == rhs.module_id && lhs.reserved == rhs.reserved && lhs.channel_id == rhs.channel_id; + } + + constexpr ALWAYS_INLINE bool operator!=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs == rhs); + } + + constexpr ALWAYS_INLINE bool operator<(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return std::tie(lhs.module_id, lhs.reserved, lhs.channel_id) < std::tie(rhs.module_id, rhs.reserved, rhs.channel_id); + } + + constexpr ALWAYS_INLINE bool operator>(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return rhs < lhs; + } + + constexpr ALWAYS_INLINE bool operator<=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs > rhs); + } + + constexpr ALWAYS_INLINE bool operator>=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs < rhs); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp new file mode 100644 index 000000000..0f88fdda6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp new file mode 100644 index 000000000..3673fd0f2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs { + + bool IsInitialized(); + + size_t GetWorkingMemorySize(int max_socket_count); + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions = SessionCountMax); + void Initialize(void *buffer, size_t buffer_size); + + void InitializeForDisableDisconnectionEmulation(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions = SessionCountMax); + void InitializeForDisableDisconnectionEmulation(void *buffer, size_t buffer_size); + + void InitializeForSystem(void *buffer, size_t buffer_size, int num_sessions); + + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp new file mode 100644 index 000000000..3931d1b2b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs { + + const HtcsPeerName GetPeerNameAny(); + const HtcsPeerName GetDefaultHostName(); + + s32 GetLastError(); + + s32 Socket(); + s32 Close(s32 desc); + s32 Connect(s32 desc, const SockAddrHtcs *address); + s32 Bind(s32 desc, const SockAddrHtcs *address); + s32 Listen(s32 desc, s32 backlog_count); + s32 Accept(s32 desc, SockAddrHtcs *address); + s32 Shutdown(s32 desc, s32 how); + s32 Fcntl(s32 desc, s32 command, s32 value); + + s32 Select(s32 count, FdSet *read, FdSet *write, FdSet *exception, TimeVal *timeout); + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags); + + void FdSetZero(FdSet *set); + void FdSetSet(s32 fd, FdSet *set); + void FdSetClr(s32 fd, FdSet *set); + bool FdSetIsSet(s32 fd, const FdSet *set); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp new file mode 100644 index 000000000..b5f3708b6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs { + + using ssize_t = intptr_t; + using AddressFamilyType = u16; + + constexpr inline int PeerNameBufferLength = 32; + constexpr inline int PortNameBufferLength = 32; + + constexpr inline int SessionCountMax = 0x10; + + constexpr inline int SocketCountMax = 40; + constexpr inline int FdSetSize = SocketCountMax; + + struct HtcsPeerName { + char name[PeerNameBufferLength]; + }; + + struct HtcsPortName { + char name[PortNameBufferLength]; + }; + + struct SockAddrHtcs { + AddressFamilyType family; + HtcsPeerName peer_name; + HtcsPortName port_name; + }; + + struct TimeVal { + s64 tv_sec; + s64 tv_usec; + }; + + struct FdSet { + int fds[FdSetSize]; + }; + + enum SocketError { + HTCS_ENONE = 0, + HTCS_EACCES = 2, + HTCS_EADDRINUSE = 3, + HTCS_EADDRNOTAVAIL = 4, + HTCS_EAGAIN = 6, + HTCS_EALREADY = 7, + HTCS_EBADF = 8, + HTCS_EBUSY = 10, + HTCS_ECONNABORTED = 13, + HTCS_ECONNREFUSED = 14, + HTCS_ECONNRESET = 15, + HTCS_EDESTADDRREQ = 17, + HTCS_EFAULT = 21, + HTCS_EINPROGRESS = 26, + HTCS_EINTR = 27, + HTCS_EINVAL = 28, + HTCS_EIO = 29, + HTCS_EISCONN = 30, + HTCS_EMFILE = 33, + HTCS_EMSGSIZE = 35, + HTCS_ENETDOWN = 38, + HTCS_ENETRESET = 39, + HTCS_ENOBUFS = 42, + HTCS_ENOMEM = 49, + HTCS_ENOTCONN = 56, + HTCS_ETIMEDOUT = 76, + HTCS_EUNKNOWN = 79, + + HTCS_EWOULDBLOCK = HTCS_EAGAIN, + }; + + enum MessageFlag { + HTCS_MSG_PEEK = 1, + HTCS_MSG_WAITALL = 2, + }; + + enum ShutdownType { + HTCS_SHUT_RD = 0, + HTCS_SHUT_WR = 1, + HTCS_SHUT_RDWR = 2, + }; + + enum FcntlOperation { + HTCS_F_GETFL = 3, + HTCS_F_SETFL = 4, + }; + + enum FcntlFlag { + HTCS_O_NONBLOCK = 4, + }; + + enum AddressFamily { + HTCS_AF_HTCS = 0, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp new file mode 100644 index 000000000..7406484ce --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs::impl { + + constexpr inline htclow::ChannelId HtcsClientChannelId = 0; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp new file mode 100644 index 000000000..b9868254c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs::impl { + + class HtcsManager; + + namespace HtcsManagerHolder { + + void AddReference(); + void Release(); + + HtcsManager *GetHtcsManager(); + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp new file mode 100644 index 000000000..20c39070a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::server { + + void Initialize(); + + void RegisterHipcServer(); + void LoopHipcServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp index 7039f3904..457de6e7a 100644 --- a/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp @@ -60,174 +60,178 @@ namespace ams::i2c { } enum I2cDevice : u32 { - I2cDevice_ClassicController = 0, - I2cDevice_Ftm3bd56 = 1, - I2cDevice_Tmp451 = 2, - I2cDevice_Nct72 = 3, - I2cDevice_Alc5639 = 4, - I2cDevice_Max77620Rtc = 5, - I2cDevice_Max77620Pmic = 6, - I2cDevice_Max77621Cpu = 7, - I2cDevice_Max77621Gpu = 8, - I2cDevice_Bq24193 = 9, - I2cDevice_Max17050 = 10, - I2cDevice_Bm92t30mwv = 11, - I2cDevice_Ina226Vdd15v0Hb = 12, + I2cDevice_ClassicController = 0, + I2cDevice_Ftm3bd56 = 1, + I2cDevice_Tmp451 = 2, + I2cDevice_Nct72 = 3, + I2cDevice_Alc5639 = 4, + I2cDevice_Max77620Rtc = 5, + I2cDevice_Max77620Pmic = 6, + I2cDevice_Max77621Cpu = 7, + I2cDevice_Max77621Gpu = 8, + I2cDevice_Bq24193 = 9, + I2cDevice_Max17050 = 10, + I2cDevice_Bm92t30mwv = 11, + I2cDevice_Ina226Vdd15v0Hb = 12, - I2cDevice_Ina226VsysCpuDs = 13, - I2cDevice_Ina226VddCpuAp = 13, + I2cDevice_Ina226VsysCpuDs = 13, + I2cDevice_Ina226VddCpuAp = 13, - I2cDevice_Ina226VsysGpuDs = 14, - I2cDevice_Ina226VddGpuAp = 14, + I2cDevice_Ina226VsysGpuDs = 14, + I2cDevice_Ina226VddGpuAp = 14, - I2cDevice_Ina226VsysDdrDs = 15, - I2cDevice_Ina226VddDdr1V1Pmic = 15, + I2cDevice_Ina226VsysDdrDs = 15, + I2cDevice_Ina226VddDdr1V1Pmic = 15, - I2cDevice_Ina226VsysAp = 16, - I2cDevice_Ina226VsysBlDs = 17, - I2cDevice_Bh1730 = 18, + I2cDevice_Ina226VsysAp = 16, + I2cDevice_Ina226VsysBlDs = 17, + I2cDevice_Bh1730 = 18, - I2cDevice_Ina226VsysCore = 19, - I2cDevice_Ina226VddCoreAp = 19, + I2cDevice_Ina226VsysCore = 19, + I2cDevice_Ina226VddCoreAp = 19, - I2cDevice_Ina226Soc1V8 = 20, - I2cDevice_Ina226VddSoc1V8 = 20, + I2cDevice_Ina226Soc1V8 = 20, + I2cDevice_Ina226VddSoc1V8 = 20, - I2cDevice_Ina226Lpddr1V8 = 21, - I2cDevice_Ina226Vdd1V8 = 21, + I2cDevice_Ina226Lpddr1V8 = 21, + I2cDevice_Ina226Vdd1V8 = 21, - I2cDevice_Ina226Reg1V32 = 22, - I2cDevice_Ina226Vdd3V3Sys = 23, - I2cDevice_HdmiDdc = 24, - I2cDevice_HdmiScdc = 25, - I2cDevice_HdmiHdcp = 26, - I2cDevice_Fan53528 = 27, - I2cDevice_Max77812_3 = 28, - I2cDevice_Max77812_2 = 29, - I2cDevice_Ina226VddDdr0V6 = 30, - I2cDevice_HoagNfcIc = 31, /* TODO */ + I2cDevice_Ina226Reg1V32 = 22, + I2cDevice_Ina226Vdd3V3Sys = 23, + I2cDevice_HdmiDdc = 24, + I2cDevice_HdmiScdc = 25, + I2cDevice_HdmiHdcp = 26, + I2cDevice_Fan53528 = 27, + I2cDevice_Max77812_3 = 28, + I2cDevice_Max77812_2 = 29, + I2cDevice_Ina226VddDdr0V6 = 30, + I2cDevice_HoagNfcIc = 31, /* TODO */ + I2cDevice_PmicUnknownAula_4_18 = 32, /* TODO */ }; /* TODO: Better place for this? */ - constexpr inline const DeviceCode DeviceCode_ClassicController = 0x350000C9; - constexpr inline const DeviceCode DeviceCode_Ftm3bd56 = 0x35000033; - constexpr inline const DeviceCode DeviceCode_Tmp451 = 0x3E000001; - constexpr inline const DeviceCode DeviceCode_Nct72 = 0x3E000001; - constexpr inline const DeviceCode DeviceCode_Alc5639 = 0x33000001; - constexpr inline const DeviceCode DeviceCode_Max77620Rtc = 0x3B000001; - constexpr inline const DeviceCode DeviceCode_Max77620Pmic = 0x3A000001; - constexpr inline const DeviceCode DeviceCode_Max77621Cpu = 0x3A000003; - constexpr inline const DeviceCode DeviceCode_Max77621Gpu = 0x3A000004; - constexpr inline const DeviceCode DeviceCode_Bq24193 = 0x39000001; - constexpr inline const DeviceCode DeviceCode_Max17050 = 0x39000033; - constexpr inline const DeviceCode DeviceCode_Bm92t30mwv = 0x040000C9; - constexpr inline const DeviceCode DeviceCode_Ina226Vdd15v0Hb = 0x3F000401; + constexpr inline const DeviceCode DeviceCode_ClassicController = 0x350000C9; + constexpr inline const DeviceCode DeviceCode_Ftm3bd56 = 0x35000033; + constexpr inline const DeviceCode DeviceCode_Tmp451 = 0x3E000001; + constexpr inline const DeviceCode DeviceCode_Nct72 = 0x3E000001; + constexpr inline const DeviceCode DeviceCode_Alc5639 = 0x33000001; + constexpr inline const DeviceCode DeviceCode_Max77620Rtc = 0x3B000001; + constexpr inline const DeviceCode DeviceCode_Max77620Pmic = 0x3A000001; + constexpr inline const DeviceCode DeviceCode_Max77621Cpu = 0x3A000003; + constexpr inline const DeviceCode DeviceCode_Max77621Gpu = 0x3A000004; + constexpr inline const DeviceCode DeviceCode_Bq24193 = 0x39000001; + constexpr inline const DeviceCode DeviceCode_Max17050 = 0x39000033; + constexpr inline const DeviceCode DeviceCode_Bm92t30mwv = 0x040000C9; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd15v0Hb = 0x3F000401; - constexpr inline const DeviceCode DeviceCode_Ina226VsysCpuDs = 0x3F000001; - constexpr inline const DeviceCode DeviceCode_Ina226VddCpuAp = 0x3F000001; + constexpr inline const DeviceCode DeviceCode_Ina226VsysCpuDs = 0x3F000001; + constexpr inline const DeviceCode DeviceCode_Ina226VddCpuAp = 0x3F000001; - constexpr inline const DeviceCode DeviceCode_Ina226VsysGpuDs = 0x3F000002; - constexpr inline const DeviceCode DeviceCode_Ina226VddGpuAp = 0x3F000002; + constexpr inline const DeviceCode DeviceCode_Ina226VsysGpuDs = 0x3F000002; + constexpr inline const DeviceCode DeviceCode_Ina226VddGpuAp = 0x3F000002; - constexpr inline const DeviceCode DeviceCode_Ina226VsysDdrDs = 0x3F000003; - constexpr inline const DeviceCode DeviceCode_Ina226VddDdr1V1Pmi = 0x3F000003; + constexpr inline const DeviceCode DeviceCode_Ina226VsysDdrDs = 0x3F000003; + constexpr inline const DeviceCode DeviceCode_Ina226VddDdr1V1Pmi = 0x3F000003; - constexpr inline const DeviceCode DeviceCode_Ina226VsysAp = 0x3F000402; - constexpr inline const DeviceCode DeviceCode_Ina226VsysBlDs = 0x3F000403; - constexpr inline const DeviceCode DeviceCode_Bh1730 = 0x35000047; + constexpr inline const DeviceCode DeviceCode_Ina226VsysAp = 0x3F000402; + constexpr inline const DeviceCode DeviceCode_Ina226VsysBlDs = 0x3F000403; + constexpr inline const DeviceCode DeviceCode_Bh1730 = 0x35000047; - constexpr inline const DeviceCode DeviceCode_Ina226VsysCore = 0x3F000404; - constexpr inline const DeviceCode DeviceCode_Ina226VddCoreAp = 0x3F000404; + constexpr inline const DeviceCode DeviceCode_Ina226VsysCore = 0x3F000404; + constexpr inline const DeviceCode DeviceCode_Ina226VddCoreAp = 0x3F000404; - constexpr inline const DeviceCode DeviceCode_Ina226Soc1V8 = 0x3F000405; - constexpr inline const DeviceCode DeviceCode_Ina226VddSoc1V8 = 0x3F000405; + constexpr inline const DeviceCode DeviceCode_Ina226Soc1V8 = 0x3F000405; + constexpr inline const DeviceCode DeviceCode_Ina226VddSoc1V8 = 0x3F000405; - constexpr inline const DeviceCode DeviceCode_Ina226Lpddr1V8 = 0x3F000406; - constexpr inline const DeviceCode DeviceCode_Ina226Vdd1V8 = 0x3F000406; + constexpr inline const DeviceCode DeviceCode_Ina226Lpddr1V8 = 0x3F000406; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd1V8 = 0x3F000406; - constexpr inline const DeviceCode DeviceCode_Ina226Reg1V32 = 0x3F000407; - constexpr inline const DeviceCode DeviceCode_Ina226Vdd3V3Sys = 0x3F000408; - constexpr inline const DeviceCode DeviceCode_HdmiDdc = 0x34000001; - constexpr inline const DeviceCode DeviceCode_HdmiScdc = 0x34000002; - constexpr inline const DeviceCode DeviceCode_HdmiHdcp = 0x34000003; - constexpr inline const DeviceCode DeviceCode_Fan53528 = 0x3A000005; - constexpr inline const DeviceCode DeviceCode_Max77812_3 = 0x3A000002; - constexpr inline const DeviceCode DeviceCode_Max77812_2 = 0x3A000006; - constexpr inline const DeviceCode DeviceCode_Ina226VddDdr0V6 = 0x3F000409; - constexpr inline const DeviceCode DeviceCode_HoagNfcIc = 0x36000001; + constexpr inline const DeviceCode DeviceCode_Ina226Reg1V32 = 0x3F000407; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd3V3Sys = 0x3F000408; + constexpr inline const DeviceCode DeviceCode_HdmiDdc = 0x34000001; + constexpr inline const DeviceCode DeviceCode_HdmiScdc = 0x34000002; + constexpr inline const DeviceCode DeviceCode_HdmiHdcp = 0x34000003; + constexpr inline const DeviceCode DeviceCode_Fan53528 = 0x3A000005; + constexpr inline const DeviceCode DeviceCode_Max77812_3 = 0x3A000002; + constexpr inline const DeviceCode DeviceCode_Max77812_2 = 0x3A000006; + constexpr inline const DeviceCode DeviceCode_Ina226VddDdr0V6 = 0x3F000409; + constexpr inline const DeviceCode DeviceCode_HoagNfcIc = 0x36000001; + constexpr inline const DeviceCode DeviceCode_PmicUnknownAula_4_18 = 0x3A000007; constexpr inline DeviceCode ConvertToDeviceCode(I2cDevice dv) { switch (dv) { - case I2cDevice_ClassicController: return DeviceCode_ClassicController; - case I2cDevice_Ftm3bd56: return DeviceCode_Ftm3bd56; - case I2cDevice_Tmp451: return DeviceCode_Tmp451; - case I2cDevice_Nct72: return DeviceCode_Nct72; - case I2cDevice_Alc5639: return DeviceCode_Alc5639; - case I2cDevice_Max77620Rtc: return DeviceCode_Max77620Rtc; - case I2cDevice_Max77620Pmic: return DeviceCode_Max77620Pmic; - case I2cDevice_Max77621Cpu: return DeviceCode_Max77621Cpu; - case I2cDevice_Max77621Gpu: return DeviceCode_Max77621Gpu; - case I2cDevice_Bq24193: return DeviceCode_Bq24193; - case I2cDevice_Max17050: return DeviceCode_Max17050; - case I2cDevice_Bm92t30mwv: return DeviceCode_Bm92t30mwv; - case I2cDevice_Ina226Vdd15v0Hb: return DeviceCode_Ina226Vdd15v0Hb; - case I2cDevice_Ina226VsysCpuDs: return DeviceCode_Ina226VsysCpuDs; - case I2cDevice_Ina226VsysGpuDs: return DeviceCode_Ina226VsysGpuDs; - case I2cDevice_Ina226VsysDdrDs: return DeviceCode_Ina226VsysDdrDs; - case I2cDevice_Ina226VsysAp: return DeviceCode_Ina226VsysAp; - case I2cDevice_Ina226VsysBlDs: return DeviceCode_Ina226VsysBlDs; - case I2cDevice_Bh1730: return DeviceCode_Bh1730; - case I2cDevice_Ina226VsysCore: return DeviceCode_Ina226VsysCore; - case I2cDevice_Ina226Soc1V8: return DeviceCode_Ina226Soc1V8; - case I2cDevice_Ina226Lpddr1V8: return DeviceCode_Ina226Lpddr1V8; - case I2cDevice_Ina226Reg1V32: return DeviceCode_Ina226Reg1V32; - case I2cDevice_Ina226Vdd3V3Sys: return DeviceCode_Ina226Vdd3V3Sys; - case I2cDevice_HdmiDdc: return DeviceCode_HdmiDdc; - case I2cDevice_HdmiScdc: return DeviceCode_HdmiScdc; - case I2cDevice_HdmiHdcp: return DeviceCode_HdmiHdcp; - case I2cDevice_Fan53528: return DeviceCode_Fan53528; - case I2cDevice_Max77812_3: return DeviceCode_Max77812_3; - case I2cDevice_Max77812_2: return DeviceCode_Max77812_2; - case I2cDevice_Ina226VddDdr0V6: return DeviceCode_Ina226VddDdr0V6; - case I2cDevice_HoagNfcIc: return DeviceCode_HoagNfcIc; + case I2cDevice_ClassicController: return DeviceCode_ClassicController; + case I2cDevice_Ftm3bd56: return DeviceCode_Ftm3bd56; + case I2cDevice_Tmp451: return DeviceCode_Tmp451; + case I2cDevice_Nct72: return DeviceCode_Nct72; + case I2cDevice_Alc5639: return DeviceCode_Alc5639; + case I2cDevice_Max77620Rtc: return DeviceCode_Max77620Rtc; + case I2cDevice_Max77620Pmic: return DeviceCode_Max77620Pmic; + case I2cDevice_Max77621Cpu: return DeviceCode_Max77621Cpu; + case I2cDevice_Max77621Gpu: return DeviceCode_Max77621Gpu; + case I2cDevice_Bq24193: return DeviceCode_Bq24193; + case I2cDevice_Max17050: return DeviceCode_Max17050; + case I2cDevice_Bm92t30mwv: return DeviceCode_Bm92t30mwv; + case I2cDevice_Ina226Vdd15v0Hb: return DeviceCode_Ina226Vdd15v0Hb; + case I2cDevice_Ina226VsysCpuDs: return DeviceCode_Ina226VsysCpuDs; + case I2cDevice_Ina226VsysGpuDs: return DeviceCode_Ina226VsysGpuDs; + case I2cDevice_Ina226VsysDdrDs: return DeviceCode_Ina226VsysDdrDs; + case I2cDevice_Ina226VsysAp: return DeviceCode_Ina226VsysAp; + case I2cDevice_Ina226VsysBlDs: return DeviceCode_Ina226VsysBlDs; + case I2cDevice_Bh1730: return DeviceCode_Bh1730; + case I2cDevice_Ina226VsysCore: return DeviceCode_Ina226VsysCore; + case I2cDevice_Ina226Soc1V8: return DeviceCode_Ina226Soc1V8; + case I2cDevice_Ina226Lpddr1V8: return DeviceCode_Ina226Lpddr1V8; + case I2cDevice_Ina226Reg1V32: return DeviceCode_Ina226Reg1V32; + case I2cDevice_Ina226Vdd3V3Sys: return DeviceCode_Ina226Vdd3V3Sys; + case I2cDevice_HdmiDdc: return DeviceCode_HdmiDdc; + case I2cDevice_HdmiScdc: return DeviceCode_HdmiScdc; + case I2cDevice_HdmiHdcp: return DeviceCode_HdmiHdcp; + case I2cDevice_Fan53528: return DeviceCode_Fan53528; + case I2cDevice_Max77812_3: return DeviceCode_Max77812_3; + case I2cDevice_Max77812_2: return DeviceCode_Max77812_2; + case I2cDevice_Ina226VddDdr0V6: return DeviceCode_Ina226VddDdr0V6; + case I2cDevice_HoagNfcIc: return DeviceCode_HoagNfcIc; + case I2cDevice_PmicUnknownAula_4_18: return DeviceCode_PmicUnknownAula_4_18; AMS_UNREACHABLE_DEFAULT_CASE(); } } constexpr inline I2cDevice ConvertToI2cDevice(DeviceCode dc) { switch (dc.GetInternalValue()) { - case DeviceCode_ClassicController.GetInternalValue(): return I2cDevice_ClassicController; - case DeviceCode_Ftm3bd56 .GetInternalValue(): return I2cDevice_Ftm3bd56; - case DeviceCode_Tmp451 .GetInternalValue(): return I2cDevice_Tmp451; - /* case DeviceCode_Nct72 .GetInternalValue(): return I2cDevice_Nct72; */ - case DeviceCode_Alc5639 .GetInternalValue(): return I2cDevice_Alc5639; - case DeviceCode_Max77620Rtc .GetInternalValue(): return I2cDevice_Max77620Rtc; - case DeviceCode_Max77620Pmic .GetInternalValue(): return I2cDevice_Max77620Pmic; - case DeviceCode_Max77621Cpu .GetInternalValue(): return I2cDevice_Max77621Cpu; - case DeviceCode_Max77621Gpu .GetInternalValue(): return I2cDevice_Max77621Gpu; - case DeviceCode_Bq24193 .GetInternalValue(): return I2cDevice_Bq24193; - case DeviceCode_Max17050 .GetInternalValue(): return I2cDevice_Max17050; - case DeviceCode_Bm92t30mwv .GetInternalValue(): return I2cDevice_Bm92t30mwv; - case DeviceCode_Ina226Vdd15v0Hb .GetInternalValue(): return I2cDevice_Ina226Vdd15v0Hb; - case DeviceCode_Ina226VsysCpuDs .GetInternalValue(): return I2cDevice_Ina226VsysCpuDs; - case DeviceCode_Ina226VsysGpuDs .GetInternalValue(): return I2cDevice_Ina226VsysGpuDs; - case DeviceCode_Ina226VsysDdrDs .GetInternalValue(): return I2cDevice_Ina226VsysDdrDs; - case DeviceCode_Ina226VsysAp .GetInternalValue(): return I2cDevice_Ina226VsysAp; - case DeviceCode_Ina226VsysBlDs .GetInternalValue(): return I2cDevice_Ina226VsysBlDs; - case DeviceCode_Bh1730 .GetInternalValue(): return I2cDevice_Bh1730; - case DeviceCode_Ina226VsysCore .GetInternalValue(): return I2cDevice_Ina226VsysCore; - case DeviceCode_Ina226Soc1V8 .GetInternalValue(): return I2cDevice_Ina226Soc1V8; - case DeviceCode_Ina226Lpddr1V8 .GetInternalValue(): return I2cDevice_Ina226Lpddr1V8; - case DeviceCode_Ina226Reg1V32 .GetInternalValue(): return I2cDevice_Ina226Reg1V32; - case DeviceCode_Ina226Vdd3V3Sys .GetInternalValue(): return I2cDevice_Ina226Vdd3V3Sys; - case DeviceCode_HdmiDdc .GetInternalValue(): return I2cDevice_HdmiDdc; - case DeviceCode_HdmiScdc .GetInternalValue(): return I2cDevice_HdmiScdc; - case DeviceCode_HdmiHdcp .GetInternalValue(): return I2cDevice_HdmiHdcp; - case DeviceCode_Fan53528 .GetInternalValue(): return I2cDevice_Fan53528; - case DeviceCode_Max77812_3 .GetInternalValue(): return I2cDevice_Max77812_3; - case DeviceCode_Max77812_2 .GetInternalValue(): return I2cDevice_Max77812_2; - case DeviceCode_Ina226VddDdr0V6 .GetInternalValue(): return I2cDevice_Ina226VddDdr0V6; - case DeviceCode_HoagNfcIc .GetInternalValue(): return I2cDevice_HoagNfcIc; + case DeviceCode_ClassicController .GetInternalValue(): return I2cDevice_ClassicController; + case DeviceCode_Ftm3bd56 .GetInternalValue(): return I2cDevice_Ftm3bd56; + case DeviceCode_Tmp451 .GetInternalValue(): return I2cDevice_Tmp451; + /* case DeviceCode_Nct72 .GetInternalValue(): return I2cDevice_Nct72; */ + case DeviceCode_Alc5639 .GetInternalValue(): return I2cDevice_Alc5639; + case DeviceCode_Max77620Rtc .GetInternalValue(): return I2cDevice_Max77620Rtc; + case DeviceCode_Max77620Pmic .GetInternalValue(): return I2cDevice_Max77620Pmic; + case DeviceCode_Max77621Cpu .GetInternalValue(): return I2cDevice_Max77621Cpu; + case DeviceCode_Max77621Gpu .GetInternalValue(): return I2cDevice_Max77621Gpu; + case DeviceCode_Bq24193 .GetInternalValue(): return I2cDevice_Bq24193; + case DeviceCode_Max17050 .GetInternalValue(): return I2cDevice_Max17050; + case DeviceCode_Bm92t30mwv .GetInternalValue(): return I2cDevice_Bm92t30mwv; + case DeviceCode_Ina226Vdd15v0Hb .GetInternalValue(): return I2cDevice_Ina226Vdd15v0Hb; + case DeviceCode_Ina226VsysCpuDs .GetInternalValue(): return I2cDevice_Ina226VsysCpuDs; + case DeviceCode_Ina226VsysGpuDs .GetInternalValue(): return I2cDevice_Ina226VsysGpuDs; + case DeviceCode_Ina226VsysDdrDs .GetInternalValue(): return I2cDevice_Ina226VsysDdrDs; + case DeviceCode_Ina226VsysAp .GetInternalValue(): return I2cDevice_Ina226VsysAp; + case DeviceCode_Ina226VsysBlDs .GetInternalValue(): return I2cDevice_Ina226VsysBlDs; + case DeviceCode_Bh1730 .GetInternalValue(): return I2cDevice_Bh1730; + case DeviceCode_Ina226VsysCore .GetInternalValue(): return I2cDevice_Ina226VsysCore; + case DeviceCode_Ina226Soc1V8 .GetInternalValue(): return I2cDevice_Ina226Soc1V8; + case DeviceCode_Ina226Lpddr1V8 .GetInternalValue(): return I2cDevice_Ina226Lpddr1V8; + case DeviceCode_Ina226Reg1V32 .GetInternalValue(): return I2cDevice_Ina226Reg1V32; + case DeviceCode_Ina226Vdd3V3Sys .GetInternalValue(): return I2cDevice_Ina226Vdd3V3Sys; + case DeviceCode_HdmiDdc .GetInternalValue(): return I2cDevice_HdmiDdc; + case DeviceCode_HdmiScdc .GetInternalValue(): return I2cDevice_HdmiScdc; + case DeviceCode_HdmiHdcp .GetInternalValue(): return I2cDevice_HdmiHdcp; + case DeviceCode_Fan53528 .GetInternalValue(): return I2cDevice_Fan53528; + case DeviceCode_Max77812_3 .GetInternalValue(): return I2cDevice_Max77812_3; + case DeviceCode_Max77812_2 .GetInternalValue(): return I2cDevice_Max77812_2; + case DeviceCode_Ina226VddDdr0V6 .GetInternalValue(): return I2cDevice_Ina226VddDdr0V6; + case DeviceCode_HoagNfcIc .GetInternalValue(): return I2cDevice_HoagNfcIc; + case DeviceCode_PmicUnknownAula_4_18.GetInternalValue(): return I2cDevice_PmicUnknownAula_4_18; AMS_UNREACHABLE_DEFAULT_CASE(); } } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp index 322a3641b..9332449d7 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp @@ -58,7 +58,7 @@ namespace ams::kvdb { /* Getters. */ size_t GetLength() const { - return strnlen(this->buffer, N); + return util::Strnlen(this->buffer, N); } const char *Get() const { @@ -72,7 +72,7 @@ namespace ams::kvdb { /* Setters. */ void Set(const char *s) { /* Ensure string can fit in our buffer. */ - CheckLength(strnlen(s, N)); + CheckLength(util::Strnlen(s, N)); std::strncpy(this->buffer, s, N); this->buffer[N - 1] = 0; } @@ -88,7 +88,7 @@ namespace ams::kvdb { /* Append to existing. */ void Append(const char *s) { const size_t length = GetLength(); - CheckLength(length + strnlen(s, N)); + CheckLength(length + util::Strnlen(s, N)); std::strncat(this->buffer, s, N - length - 1); } @@ -137,7 +137,7 @@ namespace ams::kvdb { } bool EndsWith(const char *s) const { - const size_t suffix_length = strnlen(s, N); + const size_t suffix_length = util::Strnlen(s, N); const size_t length = GetLength(); return suffix_length <= length && EndsWith(s, length - suffix_length); } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index 047562ff1..707968e7e 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp @@ -57,8 +57,8 @@ namespace ams::kvdb { public: Result Initialize(void *buffer, size_t buffer_size, size_t capacity); void Invalidate(); - std::optional TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size); - std::optional TryGetSize(const void *key, size_t key_size); + util::optional TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size); + util::optional TryGetSize(const void *key, size_t key_size); void Set(const void *key, size_t key_size, const void *value, size_t value_size); bool Contains(const void *key, size_t key_size); }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp index 32d2e9bf9..a5c1649f1 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp @@ -37,6 +37,6 @@ namespace ams::ncm { void GetTicketFileStringFromRightsId(char *dst, size_t dst_size, fs::RightsId id); void GetCertificateFileStringFromRightsId(char *dst, size_t dst_size, fs::RightsId id); - std::optional GetContentIdFromString(const char *str, size_t len); + util::optional GetContentIdFromString(const char *str, size_t len); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index cc55056bb..4c00e694e 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -94,7 +94,7 @@ namespace ams::ncm { StorageId storage_id; SystemSaveDataInfo info; sf::SharedPointer content_meta_database; - std::optional> kvs; + util::optional> kvs; ContentMetaMemoryResource *memory_resource; u32 max_content_metas; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp index 9810b7658..d185aa93a 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -303,17 +303,17 @@ namespace ams::ncm { return static_cast(this->GetHeader()->storage_id); } - std::optional GetApplicationId(const ContentMetaKey &key) const { + util::optional GetApplicationId(const ContentMetaKey &key) const { switch (key.type) { case ContentMetaType::Application: return ApplicationId{ key.id }; case ContentMetaType::Patch: return this->GetExtendedHeader()->application_id; case ContentMetaType::AddOnContent: return this->GetExtendedHeader()->application_id; case ContentMetaType::Delta: return this->GetExtendedHeader()->application_id; - default: return std::nullopt; + default: return util::nullopt; } } - std::optional GetApplicationId() const { + util::optional GetApplicationId() const { return this->GetApplicationId(this->GetKey()); } }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp index ba70fe1fc..e91309d24 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp @@ -29,7 +29,7 @@ namespace ams::ncm { struct InstallProgress { InstallProgressState state; u8 pad[3]; - TYPED_STORAGE(Result) last_result; + util::TypedStorage last_result; s64 installed_size; s64 total_size; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 8243efbb2..3e72ff483 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp @@ -131,7 +131,7 @@ namespace ams::ncm { protected: Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config); - Result PrepareContentMeta(const InstallContentMetaInfo &meta_info, std::optional key, std::optional source_version); + Result PrepareContentMeta(const InstallContentMetaInfo &meta_info, util::optional key, util::optional source_version); Result PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer); Result WritePlaceHolderBuffer(InstallContentInfo *content_info, const void *data, size_t data_size); void PrepareAgain(); @@ -145,7 +145,7 @@ namespace ams::ncm { Result PrepareSystemUpdateDependency(); virtual Result PrepareContentMetaIfLatest(const ContentMetaKey &key); /* NOTE: This is not virtual in Nintendo's code. We do so to facilitate downgrades. */ u32 GetConfig() const { return this->config; } - Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); + Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, util::optional is_temporary); StorageId GetInstallStorage() const { return this->install_storage; } @@ -164,7 +164,7 @@ namespace ams::ncm { Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys); virtual Result PrepareInstallContentMetaData() = 0; - virtual Result GetLatestVersion(std::optional *out_version, u64 id) { return ncm::ResultContentMetaNotFound(); } + virtual Result GetLatestVersion(util::optional *out_version, u64 id) { return ncm::ResultContentMetaNotFound(); } virtual Result OnExecuteComplete() { return ResultSuccess(); } @@ -187,9 +187,9 @@ namespace ams::ncm { void StartThroughputMeasurement(); void UpdateThroughputMeasurement(s64 throughput); - Result GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, std::optional source_version); + Result GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, util::optional source_version); - InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_temporary); + InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, util::optional is_temporary); Result ReadContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key); Result ListRightsIdsByInstallContentMeta(s32 *out_count, Span out_span, const InstallContentMeta &content_meta, s32 offset); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp index fdaf68217..8eb8cc088 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp @@ -31,7 +31,7 @@ namespace ams::ncm { void Inactivate(); Result Initialize(const char *package_root, const char *context_path, void *buffer, size_t buffer_size, bool requires_exfat_driver, FirmwareVariationId firmware_variation_id); - std::optional GetSystemUpdateMetaKey(); + util::optional GetSystemUpdateMetaKey(); protected: virtual Result PrepareInstallContentMetaData() override; virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp index 20923ef52..746a5ea0d 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp @@ -21,8 +21,6 @@ namespace ams::ncm { struct ProgramId { u64 value; - static const ProgramId Invalid; - inline explicit operator svc::ProgramId() const { static_assert(sizeof(value) == sizeof(svc::ProgramId)); return { this->value }; @@ -53,7 +51,6 @@ namespace ams::ncm { return lhs.value >= rhs.value; } - inline constexpr const ProgramId ProgramId::Invalid = {}; - inline constexpr const ProgramId InvalidProgramId = ProgramId::Invalid; + inline constexpr const ProgramId InvalidProgramId = {}; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp index 7154243cb..f8028b078 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -98,6 +98,10 @@ namespace ams::ncm { static const SystemProgramId Pgl; static const SystemProgramId End; + + static const SystemProgramId Manu; + static const SystemProgramId Htc; + static const SystemProgramId DevServer; }; struct AtmosphereProgramId { @@ -197,6 +201,10 @@ namespace ams::ncm { inline constexpr const SystemProgramId SystemProgramId::End = { 0x01000000000007FFul }; + inline constexpr const SystemProgramId SystemProgramId::Manu = { 0x010000000000B14Aul }; + inline constexpr const SystemProgramId SystemProgramId::Htc = { 0x010000000000B240ul }; + inline constexpr const SystemProgramId SystemProgramId::DevServer = { 0x010000000000D623ul }; + inline constexpr bool IsSystemProgramId(const ProgramId &program_id) { return (SystemProgramId::Start <= program_id && program_id <= SystemProgramId::End) || IsAtmosphereProgramId(program_id); } diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp index 33a34a706..2a48e06bf 100644 --- a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp @@ -53,6 +53,6 @@ namespace ams::os::impl { } }; - using InternalConditionVariableStorage = TYPED_STORAGE(InternalConditionVariable); + using InternalConditionVariableStorage = util::TypedStorage; } diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp index d1b0871f6..ed49fbac5 100644 --- a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp @@ -57,6 +57,6 @@ namespace ams::os::impl { } }; - using InternalCriticalSectionStorage = TYPED_STORAGE(InternalCriticalSection); + using InternalCriticalSectionStorage = util::TypedStorage; } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp index 08f1f6329..b0ddb9648 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp @@ -44,7 +44,7 @@ namespace ams::os { State_Terminated = 4, }; - TYPED_STORAGE(util::IntrusiveListNode) all_threads_node; + util::TypedStorage all_threads_node; util::TypedStorage waitlist; uintptr_t reserved[4]; u8 state; diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp index 44e465429..975445d78 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp @@ -29,7 +29,7 @@ namespace ams::os { } struct TimerEventType { - using TimeSpanStorage = TYPED_STORAGE(TimeSpan); + using TimeSpanStorage = util::TypedStorage; enum State { State_NotInitialized = 0, diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp index 7119d4ec7..5a25dd429 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp @@ -16,7 +16,88 @@ #pragma once #include #include +#include +#include namespace ams::os { + namespace impl { + + class AutoWaitableHolder { + private: + WaitableHolderType m_holder; + public: + template + ALWAYS_INLINE explicit AutoWaitableHolder(WaitableManagerType *manager, T &&arg) { + InitializeWaitableHolder(std::addressof(m_holder), std::forward(arg)); + LinkWaitableHolder(manager, std::addressof(m_holder)); + } + + ALWAYS_INLINE ~AutoWaitableHolder() { + UnlinkWaitableHolder(std::addressof(m_holder)); + FinalizeWaitableHolder(std::addressof(m_holder)); + } + + ALWAYS_INLINE std::pair ConvertResult(const std::pair result, int index) { + if (result.first == std::addressof(m_holder)) { + return std::make_pair(static_cast(nullptr), index); + } else { + return result; + } + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int) { + return std::pair(func(manager), -1); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int index, T &&x, Args &&... args) { + AutoWaitableHolder holder(manager, std::forward(x)); + return holder.ConvertResult(WaitAnyImpl(std::forward(func), manager, index + 1, std::forward(args)...), index); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, Args &&... args) { + return WaitAnyImpl(std::forward(func), manager, 0, std::forward(args)...); + } + + class TempWaitableManager { + private: + WaitableManagerType m_manager; + public: + ALWAYS_INLINE TempWaitableManager() { + os::InitializeWaitableManager(std::addressof(m_manager)); + } + + ALWAYS_INLINE ~TempWaitableManager() { + os::FinalizeWaitableManager(std::addressof(m_manager)); + } + + WaitableManagerType *Get() { + return std::addressof(m_manager); + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, Args &&... args) { + TempWaitableManager temp_manager; + return WaitAnyImpl(std::forward(func), temp_manager.Get(), 0, std::forward(args)...); + } + + using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *); + + } + + template requires (sizeof...(Args) > 0) + inline std::pair WaitAny(WaitableManagerType *manager, Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), manager, std::forward(args)...); + } + + template requires (sizeof...(Args) > 0) + inline int WaitAny(Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), std::forward(args)...).second; + } + } diff --git a/libraries/libstratosphere/include/stratosphere/pgl/pgl_event_observer.hpp b/libraries/libstratosphere/include/stratosphere/pgl/pgl_event_observer.hpp index 5e918153d..ecfcbdcbc 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/pgl_event_observer.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/pgl_event_observer.hpp @@ -19,19 +19,79 @@ #include #include #include +#include namespace ams::pgl { + namespace impl { + + class EventObserverInterface { + NON_COPYABLE(EventObserverInterface); + NON_MOVEABLE(EventObserverInterface); + public: + constexpr EventObserverInterface() = default; + + virtual ~EventObserverInterface() { /* ... */ } + + virtual Result GetSystemEvent(os::SystemEventType *out) = 0; + virtual Result GetProcessEventInfo(pm::ProcessEventInfo *out) = 0; + }; + + class EventObserverByCmif final : public EventObserverInterface { + NON_COPYABLE(EventObserverByCmif); + NON_MOVEABLE(EventObserverByCmif); + private: + ams::sf::SharedPointer m_cmif_interface; + public: + explicit EventObserverByCmif(ams::sf::SharedPointer intf) : m_cmif_interface(intf) { /* ... */ } + public: + virtual Result GetSystemEvent(os::SystemEventType *out) override { + ams::sf::CopyHandle handle; + R_TRY(m_cmif_interface->GetProcessEventHandle(std::addressof(handle))); + os::AttachSystemEvent(out, handle.GetValue(), true, svc::InvalidHandle, false, os::EventClearMode_AutoClear); + return ResultSuccess(); + } + + virtual Result GetProcessEventInfo(pm::ProcessEventInfo *out) override { + return m_cmif_interface->GetProcessEventInfo(out); + } + }; + + template requires tipc::IsIEventObserver + class EventObserverByTipc final : public EventObserverInterface { + NON_COPYABLE(EventObserverByTipc); + NON_MOVEABLE(EventObserverByTipc); + private: + T m_tipc_interface; + public: + template + explicit EventObserverByTipc(Args &&... args) : m_tipc_interface(std::forward(args)...) { /* ... */ } + public: + virtual Result GetSystemEvent(os::SystemEventType *out) override { + ams::tipc::CopyHandle handle; + R_TRY(m_tipc_interface.GetProcessEventHandle(std::addressof(handle))); + os::AttachSystemEvent(out, handle.GetValue(), true, svc::InvalidHandle, false, os::EventClearMode_AutoClear); + return ResultSuccess(); + } + + virtual Result GetProcessEventInfo(pm::ProcessEventInfo *out) override { + return m_tipc_interface.GetProcessEventInfo(ams::tipc::Out(out)); + } + }; + + } + class EventObserver { NON_COPYABLE(EventObserver); private: - ams::sf::SharedPointer interface; + std::unique_ptr m_impl; public: EventObserver() { /* ... */ } - explicit EventObserver(ams::sf::SharedPointer intf) : interface(intf) { /* ... */ } + + explicit EventObserver(std::unique_ptr impl) : m_impl(std::move(impl)) { /* ... */ } EventObserver(EventObserver &&rhs) { - this->interface = std::move(rhs.interface); + m_impl = std::move(rhs.m_impl); } EventObserver &operator=(EventObserver &&rhs) { @@ -40,18 +100,15 @@ namespace ams::pgl { } void Swap(EventObserver &rhs) { - std::swap(this->interface, rhs.interface); + std::swap(m_impl, rhs.m_impl); } public: Result GetSystemEvent(os::SystemEventType *out) { - ams::sf::CopyHandle handle; - R_TRY(this->interface->GetProcessEventHandle(std::addressof(handle))); - os::AttachSystemEvent(out, handle.GetValue(), true, svc::InvalidHandle, false, os::EventClearMode_AutoClear); - return ResultSuccess(); + return m_impl->GetSystemEvent(out); } Result GetProcessEventInfo(pm::ProcessEventInfo *out) { - return this->interface->GetProcessEventInfo(out); + return m_impl->GetProcessEventInfo(out); } }; diff --git a/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp b/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp index 1bf5e31a4..11fa19a18 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp @@ -20,8 +20,9 @@ #include #include -#define AMS_PGL_I_EVENT_OBSERVER_INTERFACE_INFO(C, H) \ +#define AMS_PGL_SF_I_EVENT_OBSERVER_INTERFACE_INFO(C, H) \ AMS_SF_METHOD_INFO(C, H, 0, Result, GetProcessEventHandle, (ams::sf::OutCopyHandle out), (out)) \ AMS_SF_METHOD_INFO(C, H, 1, Result, GetProcessEventInfo, (ams::sf::Out out), (out)) -AMS_SF_DEFINE_INTERFACE(ams::pgl::sf, IEventObserver, AMS_PGL_I_EVENT_OBSERVER_INTERFACE_INFO); +AMS_SF_DEFINE_INTERFACE(ams::pgl::sf, IEventObserver, AMS_PGL_SF_I_EVENT_OBSERVER_INTERFACE_INFO); + diff --git a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp index 723ddad5a..a9f205146 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp @@ -20,6 +20,10 @@ namespace ams::pgl::srv { - void Initialize(); + void InitializeHeap(); + void *Allocate(size_t size); + void Deallocate(void *p, size_t size); + + void StartServer(); } diff --git a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp index 1c510a1a4..abc40e59c 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp @@ -17,19 +17,39 @@ #include #include #include +#include namespace ams::pgl::srv { - class ShellInterface { - NON_COPYABLE(ShellInterface); - NON_MOVEABLE(ShellInterface); + class ShellInterfaceCommon { + NON_COPYABLE(ShellInterfaceCommon); + NON_MOVEABLE(ShellInterfaceCommon); + public: + constexpr ShellInterfaceCommon() = default; + public: + Result LaunchProgramImpl(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags); + Result TerminateProcessImpl(os::ProcessId process_id); + Result LaunchProgramFromHostImpl(os::ProcessId *out, const void *content_path, size_t content_path_size, u32 pm_flags); + Result GetHostContentMetaInfoImpl(pgl::ContentMetaInfo *out, const void *content_path, size_t content_path_size); + Result GetApplicationProcessIdImpl(os::ProcessId *out); + Result BoostSystemMemoryResourceLimitImpl(u64 size); + Result IsProcessTrackedImpl(bool *out, os::ProcessId process_id); + Result EnableApplicationCrashReportImpl(bool enabled); + Result IsApplicationCrashReportEnabledImpl(bool *out); + Result EnableApplicationAllThreadDumpOnCrashImpl(bool enabled); + Result TriggerApplicationSnapShotDumperImpl(SnapShotDumpType dump_type, const void *arg, size_t arg_size); + }; + + class ShellInterfaceCmif : public ShellInterfaceCommon { + NON_COPYABLE(ShellInterfaceCmif); + NON_MOVEABLE(ShellInterfaceCmif); private: using Allocator = ams::sf::ExpHeapAllocator; using ObjectFactory = ams::sf::ObjectFactory; private: Allocator *m_allocator; public: - constexpr ShellInterface(Allocator *a) : m_allocator(a) { /* ... */ } + constexpr ShellInterfaceCmif(Allocator *a) : ShellInterfaceCommon(), m_allocator(a) { /* ... */ } public: /* Interface commands. */ Result LaunchProgram(ams::sf::Out out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags); @@ -47,6 +67,27 @@ namespace ams::pgl::srv { Result GetShellEventObserver(ams::sf::Out> out); Result Command21NotImplemented(ams::sf::Out out, u32 in, const ams::sf::InBuffer &buf1, const ams::sf::InBuffer &buf2); }; - static_assert(pgl::sf::IsIShellInterface); + static_assert(pgl::sf::IsIShellInterface); + + class ShellInterfaceTipc : public ShellInterfaceCommon { + NON_COPYABLE(ShellInterfaceTipc); + NON_MOVEABLE(ShellInterfaceTipc); + public: + constexpr ShellInterfaceTipc() : ShellInterfaceCommon() { /* ... */ } + public: + /* Interface commands. */ + Result LaunchProgram(ams::tipc::Out out, const ncm::ProgramLocation loc, u32 pm_flags, u8 pgl_flags); + Result TerminateProcess(os::ProcessId process_id); + Result LaunchProgramFromHost(ams::tipc::Out out, const ams::tipc::InBuffer content_path, u32 pm_flags); + Result GetHostContentMetaInfo(ams::tipc::Out out, const ams::tipc::InBuffer content_path); + Result GetApplicationProcessId(ams::tipc::Out out); + Result BoostSystemMemoryResourceLimit(u64 size); + Result IsProcessTracked(ams::tipc::Out out, os::ProcessId process_id); + Result EnableApplicationCrashReport(bool enabled); + Result IsApplicationCrashReportEnabled(ams::tipc::Out out); + Result EnableApplicationAllThreadDumpOnCrash(bool enabled); + Result GetShellEventObserver(ams::tipc::OutMoveHandle out); + }; + static_assert(pgl::tipc::IsIShellInterface); } diff --git a/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_event_observer.hpp b/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_event_observer.hpp new file mode 100644 index 000000000..c35c35859 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_event_observer.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +#define AMS_PGL_TIPC_I_EVENT_OBSERVER_INTERFACE_INFO(C, H) \ + AMS_TIPC_METHOD_INFO(C, H, 0, Result, GetProcessEventHandle, (ams::tipc::OutCopyHandle out), (out)) \ + AMS_TIPC_METHOD_INFO(C, H, 1, Result, GetProcessEventInfo, (ams::tipc::Out out), (out)) + +AMS_TIPC_DEFINE_INTERFACE(ams::pgl::tipc, IEventObserver, AMS_PGL_TIPC_I_EVENT_OBSERVER_INTERFACE_INFO); + diff --git a/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_shell_interface.hpp new file mode 100644 index 000000000..a97a39bfe --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pgl/tipc/pgl_tipc_i_shell_interface.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#define AMS_PGL_TIPC_I_SHELL_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_TIPC_METHOD_INFO(C, H, 0, Result, LaunchProgram, (ams::tipc::Out out, const ncm::ProgramLocation loc, u32 pm_flags, u8 pgl_flags), (out, loc, pm_flags, pgl_flags)) \ + AMS_TIPC_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 2, Result, LaunchProgramFromHost, (ams::tipc::Out out, const ams::tipc::InBuffer content_path, u32 pm_flags), (out, content_path, pm_flags)) \ + AMS_TIPC_METHOD_INFO(C, H, 4, Result, GetHostContentMetaInfo, (ams::tipc::Out out, const ams::tipc::InBuffer content_path), (out, content_path)) \ + AMS_TIPC_METHOD_INFO(C, H, 5, Result, GetApplicationProcessId, (ams::tipc::Out out), (out)) \ + AMS_TIPC_METHOD_INFO(C, H, 6, Result, BoostSystemMemoryResourceLimit, (u64 size), (size)) \ + AMS_TIPC_METHOD_INFO(C, H, 7, Result, IsProcessTracked, (ams::tipc::Out out, os::ProcessId process_id), (out, process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 8, Result, EnableApplicationCrashReport, (bool enabled), (enabled)) \ + AMS_TIPC_METHOD_INFO(C, H, 9, Result, IsApplicationCrashReportEnabled, (ams::tipc::Out out), (out)) \ + AMS_TIPC_METHOD_INFO(C, H, 10, Result, EnableApplicationAllThreadDumpOnCrash, (bool enabled), (enabled)) \ + AMS_TIPC_METHOD_INFO(C, H, 20, Result, GetShellEventObserver, (ams::tipc::OutMoveHandle out), (out)) + +AMS_TIPC_DEFINE_INTERFACE(ams::pgl::tipc, IShellInterface, AMS_PGL_TIPC_I_SHELL_INTERFACE_INTERFACE_INFO); diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp index 7c0e3fbd6..495ad9f05 100644 --- a/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp @@ -35,7 +35,7 @@ namespace ams::powctl { struct Session { bool has_session; - TYPED_STORAGE(impl::SessionImpl) impl_storage; + util::TypedStorage impl_storage; Session() : has_session(false) { /* ... */ } }; diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp index 61196a79c..079ceb73e 100644 --- a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp @@ -19,7 +19,7 @@ namespace ams::psc { - enum PmModuleId : u16 { + enum PmModuleId : u32 { PmModuleId_Usb = 4, PmModuleId_Ethernet = 5, PmModuleId_Fgm = 6, diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson.hpp b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp new file mode 100644 index 000000000..fafc93671 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#define RAPIDJSON_NAMESPACE ams::rapidjson +#define RAPIDJSON_ASSERT(x) AMS_ABORT_UNLESS(x) + +#define RAPIDJSON_MALLOC(size) ams::MallocForRapidJson(size) +#define RAPIDJSON_REALLOC(ptr, size) ams::ReallocForRapidJson(ptr, size) +#define RAPIDJSON_FREE(ptr) ams::FreeForRapidJson(ptr) + +#include +#include +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h b/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h new file mode 100644 index 000000000..44ec5295c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h @@ -0,0 +1,284 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) { RAPIDJSON_FREE(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h new file mode 100644 index 000000000..fd6513db1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/document.h b/libraries/libstratosphere/include/stratosphere/rapidjson/document.h new file mode 100644 index 000000000..028235ec6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/document.h @@ -0,0 +1,2737 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + this->~GenericValue(); + RawAssign(rhs); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + if (newCapacity > data_.o.capacity) { + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); + data_.o.capacity = newCapacity; + } + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) + MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h new file mode 100644 index 000000000..cf046b892 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h b/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h new file mode 100644 index 000000000..50ad18bdc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h b/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h new file mode 100644 index 000000000..5d2e57b7f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h @@ -0,0 +1,122 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h b/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h new file mode 100644 index 000000000..6270da11a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h @@ -0,0 +1,216 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values + kValidateErrorType, //!< Property has a type that is not allowed by the schema.. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot //!< Property matched the sub-schema specified by 'not'. +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h new file mode 100644 index 000000000..f8bb43cb0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h new file mode 100644 index 000000000..5d89588c2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h b/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h new file mode 100644 index 000000000..d62f77f0e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h new file mode 100644 index 000000000..12455788f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h new file mode 100644 index 000000000..8fc5118aa --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h new file mode 100644 index 000000000..a40797ec2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h @@ -0,0 +1,257 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h new file mode 100644 index 000000000..621402fd3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline int CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h new file mode 100644 index 000000000..68c9e9664 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h new file mode 100644 index 000000000..9fe8c932f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h new file mode 100644 index 000000000..27092dc0d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h new file mode 100644 index 000000000..eae1a43ed --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h new file mode 100644 index 000000000..6446c403a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h new file mode 100644 index 000000000..73abd706e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h new file mode 100644 index 000000000..baecb6cc8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h @@ -0,0 +1,69 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h new file mode 100644 index 000000000..d61a67a49 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < dLen && decimals[i] >= '5') // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h new file mode 100644 index 000000000..2cf92f93a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..01437ec01 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h new file mode 100644 index 000000000..ffbc41ed1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h new file mode 100644 index 000000000..77af6c999 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h new file mode 100644 index 000000000..18111286b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h new file mode 100644 index 000000000..3d4477b9a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..11ed4d33f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h new file mode 100644 index 000000000..90e5903bc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h @@ -0,0 +1,1415 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h b/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h new file mode 100644 index 000000000..fe45df1d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h b/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h new file mode 100644 index 000000000..78aa89a0e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h @@ -0,0 +1,692 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#if defined(__has_cpp_attribute) +# if __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT_ASSERT(x) +#else +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h b/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h new file mode 100644 index 000000000..09ace4eba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h @@ -0,0 +1,2244 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h b/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h new file mode 100644 index 000000000..11f716096 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h @@ -0,0 +1,2644 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +/*! \see + */ +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0; + virtual void Disallowed() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : + factory(f), + error_handler(eh), + schema(s), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0) + { + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else + oneValid = true; + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue URIType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + else if (refEntry->schema) + *refEntry->schema = typeless_; + + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + RAPIDJSON_DELETE(ownAllocator_); + } + + const URIType& GetURI() const { return uri_; } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = typeless_; + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + URIType uri_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString(); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) { + AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h new file mode 100644 index 000000000..1fd70915c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h @@ -0,0 +1,223 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h new file mode 100644 index 000000000..82ad3ca6b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h new file mode 100644 index 000000000..8b389219a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h @@ -0,0 +1,710 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libraries/libstratosphere/include/stratosphere/scs.hpp b/libraries/libstratosphere/include/stratosphere/scs.hpp new file mode 100644 index 000000000..e3a7902fd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +#include +#include + +#include diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp new file mode 100644 index 000000000..adfe97b39 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::scs { + + struct CommandHeader { + u64 id; + u32 command; + u32 body_size; + }; + + struct ResponseHeader { + u64 id; + u32 response; + u32 body_size; + }; + + class CommandProcessor { + protected: + enum Command { + Command_None = 0, + Command_LaunchProgramFromHost = 1, + Command_TerminateProcesses = 2, + Command_GetFirmwareVersion = 3, + Command_Reboot = 4, + Command_SetSafeMode = 5, + Command_RegisterTenvDefinitionFilePath = 6, + Command_TerminateApplication = 7, + Command_Shutdown = 8, + Command_SubscribeProcessEvent = 9, + Command_GetTitleName = 10, + Command_ControlVirtualTemperature = 11, + Command_LaunchInstalledApplication = 12, + Command_LaunchGameCardApplication = 13, + Command_LaunchInstalledSystemProcess = 14, + Command_TakeScreenShot = 15, + Command_TakeForegroundScreenShot = 16, + Command_SimulateGameCardDetection = 17, + Command_SimulateSdCardDetection = 18, + Command_DumpRunningApplication = 19, + }; + + enum Response { + Response_None = 0, + Response_Success = 1, + Response_Error = 2, + /* ... */ + }; + public: + constexpr CommandProcessor() = default; + + void Initialize(); + public: + virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket); + protected: + static void SendSuccess(s32 socket, const CommandHeader &header); + static void SendErrorResult(s32 socket, const CommandHeader &header, Result result); + private: + static void SendErrorResult(s32 socket, u64 id, Result result); + + static void OnProcessStart(u64 id, s32 socket, os::ProcessId process_id); + static void OnProcessExit(u64 id, s32 socket, os::ProcessId process_id); + static void OnProcessJitDebug(u64 id, s32 socket, os::ProcessId process_id); + }; + + os::SdkMutex &GetHtcsSendMutex(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp new file mode 100644 index 000000000..b86865c31 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::scs { + + enum Port { + Port_HtcTenv, + Port_Count, + }; + + constexpr inline int SessionCount[Port_Count] = { + 6, + }; + + constexpr inline auto MaxSessions = [] { + auto total = 0; + for (const auto sessions : SessionCount) { + total += sessions; + } + return total; + }(); + + struct ServerOptions { + static constexpr size_t PointerBufferSize = 0; + static constexpr size_t MaxDomains = 6; + static constexpr size_t MaxDomainObjects = 16; + }; + + class ServerManager final : public sf::hipc::ServerManager { + /* ... */ + }; + + ServerManager *GetServerManager(); + void StartServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp new file mode 100644 index 000000000..9ba1caf22 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::scs { + + using ProcessEventHandler = void(*)(u64 id, s32 socket, os::ProcessId process_id); + + void InitializeShell(); + + void RegisterCommonProcessEventHandler(ProcessEventHandler on_start, ProcessEventHandler on_exit, ProcessEventHandler on_jit_debug); + + bool RegisterSocket(s32 socket); + void UnregisterSocket(s32 socket); + + Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags); + + Result SubscribeProcessEvent(s32 socket, bool is_register, u64 id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp new file mode 100644 index 000000000..2cba7808e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::scs { + + class ShellServer { + private: + htcs::HtcsPortName m_port_name; + os::ThreadType m_thread; + u8 m_buffer[64_KB]; + CommandProcessor *m_command_processor; + private: + static void ThreadEntry(void *arg) { reinterpret_cast(arg)->DoShellServer(); } + + void DoShellServer(); + public: + constexpr ShellServer() = default; + public: + void Initialize(const char *port_name, void *stack, size_t stack_size, CommandProcessor *command_processor); + void Start(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp new file mode 100644 index 000000000..8a06ceb3e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::scs { + + void InitializeTenvServiceManager(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings.hpp b/libraries/libstratosphere/include/stratosphere/settings.hpp index 5b995f595..5f0419aaa 100644 --- a/libraries/libstratosphere/include/stratosphere/settings.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp new file mode 100644 index 000000000..4f46473e0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::factory { + + struct ConfigurationId1 { + char str[30]; + }; + static_assert(sizeof(ConfigurationId1) == 30); + static_assert(util::is_pod::value); + + void GetConfigurationId1(ConfigurationId1 *out); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp index 301139cca..cfb096004 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp @@ -24,4 +24,6 @@ namespace ams::settings::factory { static_assert(sizeof(SerialNumber) == 0x18); static_assert(util::is_pod::value); + Result GetSerialNumber(SerialNumber *out); + } diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 99b8e201c..e59d54caf 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -77,8 +77,8 @@ namespace ams::sf::cmif { virtual ServiceObjectHolder GetObject(DomainObjectId id) override final; }; public: - using DomainEntryStorage = TYPED_STORAGE(Entry); - using DomainStorage = TYPED_STORAGE(Domain); + using DomainEntryStorage = util::TypedStorage; + using DomainStorage = util::TypedStorage; private: class EntryManager { private: @@ -127,7 +127,8 @@ namespace ams::sf::cmif { if (storage == nullptr) { return nullptr; } - return new (storage) Domain(this); + + return std::construct_at(static_cast(storage), this); } public: static void DestroyDomainServiceObject(DomainServiceObject *obj) { diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp index 47b359866..827147576 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp @@ -54,7 +54,10 @@ namespace ams::sf::cmif { Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data); constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const { - return this->cmd_id == cmd_id && this->hosver_low <= hosver && hosver <= this->hosver_high; + const bool min_valid = this->hosver_low == hos::Version_Min; + const bool max_valid = this->hosver_high == hos::Version_Max; + + return this->cmd_id == cmd_id && (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high); } constexpr inline decltype(handler) GetHandler() const { diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp index d8f1c3ea1..cc9340caa 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp @@ -70,11 +70,11 @@ namespace ams::sf::cmif { /* Boolean operators. */ explicit constexpr operator bool() const { - return this->dispatch_meta != nullptr; + return this->srv != nullptr; } constexpr bool operator!() const { - return this->dispatch_meta == nullptr; + return this->srv == nullptr; } /* Getters. */ diff --git a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp index 19986c90a..d611f5d1b 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp @@ -260,9 +260,9 @@ namespace ams::sf::hipc { private: /* Resource storage. */ os::Mutex resource_mutex; - TYPED_STORAGE(Server) server_storages[MaxServers]; + util::TypedStorage server_storages[MaxServers]; bool server_allocated[MaxServers]; - TYPED_STORAGE(ServerSession) session_storages[MaxSessions]; + util::TypedStorage session_storages[MaxSessions]; bool session_allocated[MaxSessions]; u8 pointer_buffer_storage[0x10 + (MaxSessions * ManagerOptions::PointerBufferSize)]; u8 saved_message_storage[0x10 + (MaxSessions * hipc::TlsMessageBufferSize)]; diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index d403aa23d..19f760b75 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -412,9 +412,13 @@ namespace ams::sf::impl { size_t out_object_index; }; + template + using DecayForCommandMetaArguments = typename std::conditional::type> && !std::is_base_of::type>::value, T, typename std::decay::type>::type; + template struct CommandMetaInfo { public: + using ArgsTypeForInvoke = std::tuple...>; using ArgsType = std::tuple::type...>; using InDatas = TupleFilter::FilteredType; @@ -628,8 +632,9 @@ namespace ams::sf::impl { private: MoveHandle move_handles[NumMove]; CopyHandle copy_handles[NumCopy]; + bool copy_managed[NumCopy]; public: - constexpr OutHandleHolder() : move_handles(), copy_handles() { /* ... */ } + constexpr OutHandleHolder() : move_handles(), copy_handles(), copy_managed() { /* ... */ } template constexpr inline MoveHandle *GetMoveHandlePointer() { @@ -643,8 +648,15 @@ namespace ams::sf::impl { return ©_handles[Index]; } - constexpr inline void CopyTo(const HipcRequest &response, const size_t num_out_object_handles) { - #define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { response.copy_handles[n] = copy_handles[n].GetValue(); } } while (0) + template + constexpr inline bool *GetCopyHandleManagedPointer() { + static_assert(Index < NumCopy, "Index < NumCopy"); + return ©_managed[Index]; + } + + constexpr inline void CopyTo(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, const size_t num_out_object_handles) { + ctx.handles_to_close->num_handles = 0; + #define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { const auto handle = copy_handles[n].GetValue(); response.copy_handles[n] = handle; if (copy_managed[n]) { ctx.handles_to_close->handles[ctx.handles_to_close->num_handles++] = handle; } } } while (0) _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(0); _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(1); _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(2); @@ -672,7 +684,7 @@ namespace ams::sf::impl { private: std::array in_object_holders; std::array out_object_holders; - std::array), NumOutObjects> out_shared_pointers; + std::array>, NumOutObjects> out_shared_pointers; std::array out_object_ids; public: constexpr InOutObjectHolder() : in_object_holders(), out_object_holders() { @@ -720,12 +732,12 @@ namespace ams::sf::impl { template SharedPointer *GetOutObjectSharedPointer() { static_assert(sizeof(SharedPointer) == sizeof(SharedPointer)); - return static_cast *>(static_cast(&out_shared_pointers[Index])); + return static_cast *>(static_cast(GetPointer(out_shared_pointers[Index]))); } template Out> GetOutObject() { - auto sp = new (GetOutObjectSharedPointer()) SharedPointer; + auto sp = std::construct_at(GetOutObjectSharedPointer()); return Out>(sp, &this->out_object_ids[Index]); } @@ -814,7 +826,8 @@ namespace ams::sf::impl { } /* Useful defines. */ - using ArgsType = typename CommandMeta::ArgsType; + using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; + using ArgsType = typename CommandMeta::ArgsType; using BufferArrayType = std::array; using OutRawHolderType = OutRawHolder; using OutHandleHolderType = OutHandleHolder; @@ -1001,7 +1014,7 @@ namespace ams::sf::impl { /* Argument deserialization. */ private: template::type> - NX_CONSTEXPR T DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { + NX_CONSTEXPR typename std::tuple_element::type DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index]; if constexpr (Info.arg_type == ArgumentType::InData) { /* New in rawdata. */ @@ -1030,7 +1043,7 @@ namespace ams::sf::impl { if constexpr (std::is_same>::value) { return T(out_handles_holder.template GetMoveHandlePointer()); } else if constexpr (std::is_same>::value) { - return T(out_handles_holder.template GetCopyHandlePointer()); + return T(out_handles_holder.template GetCopyHandlePointer(), out_handles_holder.template GetCopyHandleManagedPointer()); } else { static_assert(!std::is_same::value, "Invalid OutHandle kind"); } @@ -1047,6 +1060,8 @@ namespace ams::sf::impl { constexpr auto Attributes = CommandMeta::BufferAttributes[Info.buffer_index]; if constexpr (Attributes & SfBufferAttr_In) { /* TODO: AMS_ABORT_UNLESS()? N does not bother. */ + using InvokeType = typename std::tuple_element::type; + static_assert(std::same_as); return *reinterpret_cast(buffers[Info.buffer_index].GetAddress()); } else if constexpr (Attributes & SfBufferAttr_Out) { return T(buffers[Info.buffer_index]); @@ -1063,12 +1078,12 @@ namespace ams::sf::impl { } template - NX_CONSTEXPR ArgsType DeserializeArgumentsImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder, std::index_sequence) { - return ArgsType { DeserializeArgumentImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder)..., }; + NX_CONSTEXPR ArgsTypeForInvoke DeserializeArgumentsImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder, std::index_sequence) { + return ArgsTypeForInvoke { DeserializeArgumentImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder)..., }; } public: - NX_CONSTEXPR ArgsType DeserializeArguments(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { - return DeserializeArgumentsImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder, std::make_index_sequence::value>{}); + NX_CONSTEXPR ArgsTypeForInvoke DeserializeArguments(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { + return DeserializeArgumentsImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder, std::make_index_sequence::value>{}); } }; @@ -1118,16 +1133,16 @@ namespace ams::sf::impl { /* Decoding/Invocation. */ { - typename CommandMeta::ArgsType args_tuple = ImplProcessorType::DeserializeArguments(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder); + typename CommandMeta::ArgsTypeForInvoke args_tuple = ImplProcessorType::DeserializeArguments(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder); /* Handle in process ID holder if relevant. */ if constexpr (CommandMeta::HasInProcessIdHolder) { /* TODO: More precise value than 32? */ - static_assert(std::tuple_size::value <= 32, "Commands must have <= 32 arguments"); + static_assert(std::tuple_size::value <= 32, "Commands must have <= 32 arguments"); os::ProcessId process_id{ctx.request.pid}; #define _SF_IMPL_PROCESSOR_MARSHAL_PROCESS_ID(n) do { \ - using ArgsType = typename CommandMeta::ArgsType; \ - if constexpr (n < std::tuple_size::value) { \ + using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; \ + if constexpr (n < std::tuple_size::value) { \ if constexpr (CommandMeta::template IsInProcessIdHolderIndex) { \ R_TRY(MarshalProcessId(std::get(args_tuple), process_id)); \ } \ @@ -1149,7 +1164,7 @@ namespace ams::sf::impl { sf::IServiceObject * const this_ptr = ctx.srv_obj; const auto command_result = [this_ptr, invoke_impl, &args_tuple](std::index_sequence) ALWAYS_INLINE_LAMBDA { return invoke_impl(this_ptr, std::forward::type>(std::get(args_tuple))...); - }(std::make_index_sequence::value>()); + }(std::make_index_sequence::value>()); if (R_FAILED(command_result)) { cmif::PointerAndSize out_raw_data; @@ -1172,7 +1187,7 @@ namespace ams::sf::impl { ImplProcessorType::SetOutBuffers(response, buffers, is_buffer_map_alias); /* Set out handles. */ - out_handles_holder.CopyTo(response, runtime_metadata.GetOutObjectCount()); + out_handles_holder.CopyTo(ctx, response, runtime_metadata.GetOutObjectCount()); /* Set output objects. */ #define _SF_IMPL_PROCESSOR_MARSHAL_OUT_OBJECT(n) do { \ diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp index 24d9dc526..438e88f67 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp @@ -206,11 +206,11 @@ namespace ams::sf { } constexpr explicit operator Span() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } constexpr Span ToSpan() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } }; @@ -238,11 +238,11 @@ namespace ams::sf { } constexpr explicit operator Span() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } constexpr Span ToSpan() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } }; @@ -256,6 +256,8 @@ namespace ams::sf { using InNonSecureBuffer = typename impl::InBufferImpl; using InNonDeviceBuffer = typename impl::InBufferImpl; + using InNonSecureAutoSelectBuffer = typename impl::InBufferImpl; + using OutBuffer = typename impl::OutBufferImpl; using OutMapAliasBuffer = typename impl::OutBufferImpl; using OutPointerBuffer = typename impl::OutBufferImpl; @@ -263,6 +265,8 @@ namespace ams::sf { using OutNonSecureBuffer = typename impl::OutBufferImpl; using OutNonDeviceBuffer = typename impl::OutBufferImpl; + using OutNonSecureAutoSelectBuffer = typename impl::OutBufferImpl; + template using InArray = typename impl::InArrayImpl; template diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp index 52999127e..047a18737 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp @@ -133,8 +133,11 @@ namespace ams::sf { private: using T = CopyHandle; using Base = impl::OutHandleImpl; + private: + bool *m_managed; public: - constexpr Out(T *p) : Base(p) { /* ... */ } + constexpr Out(T *p) : Base(p), m_managed(nullptr) { /* ... */ } + constexpr Out(T *p, bool *m) : Base(p), m_managed(m) { /* ... */ } constexpr void SetValue(const Handle &value) { Base::SetValue(value); @@ -144,6 +147,11 @@ namespace ams::sf { Base::SetValue(value); } + constexpr void SetManaged(bool m) { + AMS_ASSERT(m_managed != nullptr); + *m_managed = m; + } + constexpr const T &GetValue() const { return Base::GetValue(); } diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_object_factory.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_object_factory.hpp index 5e44c2bed..0f59564c7 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_object_factory.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_object_factory.hpp @@ -43,7 +43,7 @@ namespace ams::sf { using Type = Impl; }; - template requires (std::is_abstract::value ) + template requires (std::is_abstract::value) struct UnmanagedEmplaceImplHolderBaseGetter { class Impl2 : public Impl { public: diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp index cf6ffe24b..059eca8db 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp @@ -113,7 +113,7 @@ namespace ams::sf { void DisposeImpl() { Allocator *a = this->GetAllocator(); - this->~Object(); + std::destroy_at(this); operator delete(this, a); } public: diff --git a/libraries/libstratosphere/include/stratosphere/sm.hpp b/libraries/libstratosphere/include/stratosphere/sm.hpp index 98e3f83ac..df44d6990 100644 --- a/libraries/libstratosphere/include/stratosphere/sm.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm.hpp @@ -19,10 +19,8 @@ #include #include #include -#include #include #include #include -#include diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp deleted file mode 100644 index 4afa5fdc5..000000000 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include -#include - -#define AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetRecord, (sf::Out record, sm::ServiceName service), (record, service)) \ - AMS_SF_METHOD_INFO(C, H, 65001, void, AtmosphereListRecords, (const sf::OutArray &records, sf::Out out_count, u64 offset), (records, out_count, offset)) \ - AMS_SF_METHOD_INFO(C, H, 65002, void, AtmosphereGetRecordSize, (sf::Out record_size), (record_size)) - -AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IDebugMonitorInterface, AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp index 8c6d11a95..542bb7b69 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp @@ -17,13 +17,13 @@ #pragma once #include #include -#include +#include -#define AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterProcess, (os::ProcessId process_id, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac), (process_id, acid_sac, aci_sac)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id)) \ - AMS_SF_METHOD_INFO(C, H, 65000, void, AtmosphereEndInitDefers, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 65001, void, AtmosphereHasMitm, (sf::Out out, sm::ServiceName service), (out, service)) \ - AMS_SF_METHOD_INFO(C, H, 65002, Result, AtmosphereRegisterProcess, (os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac), (process_id, program_id, override_status, acid_sac, aci_sac)) +#define AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterProcess, (os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac), (process_id, acid_sac, aci_sac)) \ + AMS_TIPC_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 65000, void, AtmosphereEndInitDefers, (), ()) \ + AMS_TIPC_METHOD_INFO(C, H, 65001, void, AtmosphereHasMitm, (tipc::Out out, sm::ServiceName service), (out, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65002, Result, AtmosphereRegisterProcess, (os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac), (process_id, program_id, override_status, acid_sac, aci_sac)) -AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IManagerInterface, AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO) +AMS_TIPC_DEFINE_INTERFACE(ams::sm::impl, IManagerInterface, AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp index f833abe3a..1001fbe0d 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp @@ -17,22 +17,22 @@ #pragma once #include #include -#include +#include -#define AMS_SM_I_USER_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterClient, (const sf::ClientProcessId &client_process_id), (client_process_id)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (sf::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, RegisterService, (sf::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service)) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, DetachClient, (const sf::ClientProcessId &client_process_id), (client_process_id)) \ - AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereInstallMitm, (sf::OutMoveHandle srv_h, sf::OutMoveHandle qry_h, sm::ServiceName service), (srv_h, qry_h, service)) \ - AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereUninstallMitm, (sm::ServiceName service), (service)) \ - AMS_SF_METHOD_INFO(C, H, 65003, Result, AtmosphereAcknowledgeMitmSession, (sf::Out client_info, sf::OutMoveHandle fwd_h, sm::ServiceName service), (client_info, fwd_h, service)) \ - AMS_SF_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (sf::Out out, sm::ServiceName service), (out, service)) \ - AMS_SF_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (sm::ServiceName service), (service)) \ - AMS_SF_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (sm::ServiceName service), (service)) \ - AMS_SF_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (sm::ServiceName service), (service)) \ - AMS_SF_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (sf::Out out, sm::ServiceName service), (out, service)) \ - AMS_SF_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (sm::ServiceName service), (service)) +#define AMS_SM_I_USER_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterClient, (const tipc::ClientProcessId client_process_id), (client_process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (tipc::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 2, Result, RegisterService, (tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \ + AMS_TIPC_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service)) \ + AMS_TIPC_METHOD_INFO(C, H, 4, Result, DetachClient, (const tipc::ClientProcessId client_process_id), (client_process_id)) \ + AMS_TIPC_METHOD_INFO(C, H, 65000, Result, AtmosphereInstallMitm, (tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, sm::ServiceName service), (srv_h, qry_h, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65001, Result, AtmosphereUninstallMitm, (sm::ServiceName service), (service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65003, Result, AtmosphereAcknowledgeMitmSession, (tipc::Out client_info, tipc::OutMoveHandle fwd_h, sm::ServiceName service), (client_info, fwd_h, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (tipc::Out out, sm::ServiceName service), (out, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (sm::ServiceName service), (service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (sm::ServiceName service), (service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (sm::ServiceName service), (service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (tipc::Out out, sm::ServiceName service), (out, service)) \ + AMS_TIPC_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (sm::ServiceName service), (service)) -AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IUserInterface, AMS_SM_I_USER_INTERFACE_INTERFACE_INFO) +AMS_TIPC_DEFINE_INTERFACE(ams::sm::impl, IUserInterface, AMS_SM_I_USER_INTERFACE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_api.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_api.hpp index 76dbf6e36..e14a417c0 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_api.hpp @@ -20,6 +20,10 @@ namespace ams::sm { + /* Initialization. */ + Result Initialize(); + Result Finalize(); + /* Ordinary SM API. */ Result GetService(Service *out, ServiceName name); Result RegisterService(Handle *out, ServiceName name, size_t max_sessions, bool is_light); @@ -29,17 +33,4 @@ namespace ams::sm { Result HasService(bool *out, ServiceName name); Result WaitService(ServiceName name); - /* Scoped session access. */ - namespace impl { - - void DoWithSessionImpl(void (*Invoker)(void *), void *Function); - - } - - template - NX_CONSTEXPR void DoWithSession(F f) { - auto invoker = +[](void *func) { (*(F *)func)(); }; - impl::DoWithSessionImpl(invoker, &f); - } - } diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp deleted file mode 100644 index 952f44b27..000000000 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "sm_api.hpp" - -namespace ams::sm { - - /* Utility, for scoped access to libnx services. */ - template - class ScopedServiceHolder { - NON_COPYABLE(ScopedServiceHolder); - private: - Result result; - bool has_initialized; - public: - ScopedServiceHolder(bool initialize = true) : result(ResultSuccess()), has_initialized(false) { - if (initialize) { - this->Initialize(); - } - } - - ~ScopedServiceHolder() { - if (this->has_initialized) { - this->Finalize(); - } - } - - ScopedServiceHolder(ScopedServiceHolder&& rhs) { - this->result = rhs.result; - this->has_initialized = rhs.has_initialized; - rhs.result = ResultSuccess(); - rhs.has_initialized = false; - } - - ScopedServiceHolder &operator=(ScopedServiceHolder&& rhs) { - rhs.Swap(*this); - return *this; - } - - void Swap(ScopedServiceHolder &rhs) { - std::swap(this->result, rhs.result); - std::swap(this->has_initialized, rhs.has_initialized); - } - - explicit operator bool() const { - return this->has_initialized; - } - - Result Initialize() { - AMS_ABORT_UNLESS(!this->has_initialized); - - sm::DoWithSession([&]() { - if constexpr (std::is_same::value) { - Initializer(); - this->result = ResultSuccess(); - } else { - this->result = Initializer(); - } - }); - - this->has_initialized = R_SUCCEEDED(this->result); - return this->result; - } - - void Finalize() { - AMS_ABORT_UNLESS(this->has_initialized); - Finalizer(); - this->has_initialized = false; - } - - Result GetResult() const { - return this->result; - } - }; - -} diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp index 3a9dc531d..92e83f414 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp @@ -44,7 +44,9 @@ namespace ams::sm { return Encode(name, std::strlen(name)); } }; - static constexpr ServiceName InvalidServiceName = ServiceName::Encode(""); + + static constexpr inline ServiceName InvalidServiceName = ServiceName::Encode(""); + static_assert(alignof(ServiceName) == 1, "ServiceName definition!"); inline bool operator==(const ServiceName &lhs, const ServiceName &rhs) { @@ -55,18 +57,6 @@ namespace ams::sm { return !(lhs == rhs); } - /* For Debug Monitor extensions. */ - struct ServiceRecord { - ServiceName service; - os::ProcessId owner_process_id; - u64 max_sessions; - os::ProcessId mitm_process_id; - os::ProcessId mitm_waiting_ack_process_id; - bool is_light; - bool mitm_waiting_ack; - }; - static_assert(sizeof(ServiceRecord) == 0x30, "ServiceRecord definition!"); - /* For Mitm extensions. */ struct MitmProcessInfo { os::ProcessId process_id; diff --git a/libraries/libstratosphere/include/stratosphere/socket.hpp b/libraries/libstratosphere/include/stratosphere/socket.hpp index f42d359b9..29c9af1c9 100644 --- a/libraries/libstratosphere/include/stratosphere/socket.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket.hpp @@ -18,5 +18,9 @@ #include #include #include +#include #include +#include +#include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp index cc99f0aa8..a8de2535c 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp @@ -16,7 +16,9 @@ #pragma once #include #include +#include #include +#include namespace ams::socket { @@ -28,4 +30,30 @@ namespace ams::socket { u32 InetNtohl(u32 net); u16 InetNtohs(u16 net); + Result Initialize(const Config &config); + Result Finalize(); + + Result InitializeAllocatorForInternal(void *buffer, size_t size); + + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len); + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags); + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags); + + s32 Shutdown(s32 desc, ShutdownMethod how); + + s32 SocketExempt(Family domain, Type type, Protocol protocol); + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 Bind(s32 desc, const SockAddr *address, SockLenT len); + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size); + + s32 Listen(s32 desc, s32 backlog); + + s32 Close(s32 desc); + + } diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp new file mode 100644 index 000000000..0110cd967 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::socket { + + constexpr ALWAYS_INLINE size_t AlignMss(size_t size) { + return util::DivideUp(size, static_cast(1500)) * static_cast(1500); + } + + class Config { + private: + u32 m_version; + protected: + bool m_system; + bool m_smbp; + void *m_memory_pool; + size_t m_memory_pool_size; + size_t m_allocator_pool_size; + size_t m_tcp_initial_send_buffer_size; + size_t m_tcp_initial_receive_buffer_size; + size_t m_tcp_auto_send_buffer_size_max; + size_t m_tcp_auto_receive_buffer_size_max; + size_t m_udp_send_buffer_size; + size_t m_udp_receive_buffer_size; + int m_sb_efficiency; + int m_concurrency_count_max; + public: + constexpr Config(void *mp, size_t mp_sz, size_t ap, size_t is, size_t ir, size_t as, size_t ar, size_t us, size_t ur, int sbe, int c) + : m_version(LibraryVersion), + m_system(false), + m_smbp(false), + m_memory_pool(mp), + m_memory_pool_size(mp_sz), + m_allocator_pool_size(ap), + m_tcp_initial_send_buffer_size(is), + m_tcp_initial_receive_buffer_size(ir), + m_tcp_auto_send_buffer_size_max(as), + m_tcp_auto_receive_buffer_size_max(ar), + m_udp_send_buffer_size(us), + m_udp_receive_buffer_size(ur), + m_sb_efficiency(sbe), + m_concurrency_count_max(c) + { + /* ... */ + } + + constexpr u32 GetVersion() const { return m_version; } + constexpr bool IsSystemClient() const { return m_system; } + constexpr bool IsSmbpClient() const { return m_smbp; } + constexpr void *GetMemoryPool() const { return m_memory_pool; } + constexpr size_t GetMemoryPoolSize() const { return m_memory_pool_size; } + constexpr size_t GetAllocatorPoolSize() const { return m_allocator_pool_size; } + constexpr size_t GetTcpInitialSendBufferSize() const { return m_tcp_initial_send_buffer_size; } + constexpr size_t GetTcpInitialReceiveBufferSize() const { return m_tcp_initial_receive_buffer_size; } + constexpr size_t GetTcpAutoSendBufferSizeMax() const { return m_tcp_auto_send_buffer_size_max; } + constexpr size_t GetTcpAutoReceiveBufferSizeMax() const { return m_tcp_auto_receive_buffer_size_max; } + constexpr size_t GetUdpSendBufferSize() const { return m_udp_send_buffer_size; } + constexpr size_t GetUdpReceiveBufferSize() const { return m_udp_receive_buffer_size; } + constexpr int GetSocketBufferEfficiency() const { return m_sb_efficiency; } + constexpr int GetConcurrencyCountMax() const { return m_concurrency_count_max; } + + constexpr void SetTcpInitialSendBufferSize(size_t size) { m_tcp_initial_send_buffer_size = size; } + constexpr void SetTcpInitialReceiveBufferSize(size_t size) { m_tcp_initial_receive_buffer_size = size; } + constexpr void SetTcpAutoSendBufferSizeMax(size_t size) { m_tcp_auto_send_buffer_size_max = size; } + constexpr void SetTcpAutoReceiveBufferSizeMax(size_t size) { m_tcp_auto_receive_buffer_size_max = size; } + constexpr void SetUdpSendBufferSize(size_t size) { m_udp_send_buffer_size = size; } + constexpr void SetUdpReceiveBufferSize(size_t size) { m_udp_receive_buffer_size = size; } + constexpr void SetSocketBufferEfficiency(int sb) { AMS_ABORT_UNLESS(1 <= sb && sb <= 8); m_sb_efficiency = sb; } + constexpr void SetConcurrencyCountMax(int c) { m_concurrency_count_max = c; } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp new file mode 100644 index 000000000..122e38f38 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::socket { + + constexpr inline s32 InvalidSocket = -1; + constexpr inline s32 SocketError = -1; + + constexpr inline auto DefaultTcpAutoBufferSizeMax = 192_KB; + constexpr inline auto MinTransferMemorySize = (2 * DefaultTcpAutoBufferSizeMax + 128_KB); + constexpr inline auto MinSocketAllocatorSize = 128_KB; + constexpr inline auto MinSocketMemoryPoolSize = MinSocketAllocatorSize + MinTransferMemorySize; + constexpr inline auto MinMemHeapAllocatorSize = 16_KB; + constexpr inline auto MinimumSharedMbufPoolReservation = 4_KB; + + constexpr inline size_t MemoryPoolAlignment = 4_KB; + + constexpr inline auto ConcurrencyLimitMax = 14; + + /* TODO: Does this need to be 1 for sockets to work on lower firmware versions? */ + /* Is this value actually used/checked by bsdsockets sysmodule? */ + constexpr inline auto LibraryVersion = 7; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp index faf8b2f9d..ce5513942 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp @@ -19,10 +19,21 @@ namespace ams::socket { enum class Errno : u32 { - ESuccess = 0, + ESuccess = 0, /* ... */ - ENoSpc = 28, + EAgain = 11, + ENoMem = 12, /* ... */ + EFault = 14, + /* ... */ + EInval = 22, + /* ... */ + ENoSpc = 28, + /* ... */ + EL3Hlt = 46, + /* ... */ + EOpNotSupp = 95, + ENotSup = EOpNotSupp, }; enum class HErrno : s32 { diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp new file mode 100644 index 000000000..b7608745e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::socket { + + enum class Level : s32 { + Sol_Ip = 0, + Sol_Icmp = 1, + Sol_Tcp = 6, + Sol_Udp = 17, + Sol_UdpLite = 136, + + Sol_Socket = 0xFFFF, + }; + + enum class Option : u32 { + So_Debug = (1 << 0), + /* ... */ + So_ReuseAddr = (1 << 2), + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp new file mode 100644 index 000000000..19b6615e3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::socket { + + class SystemConfigDefault : public Config { + public: + static constexpr size_t DefaultTcpInitialSendBufferSize = 32_KB; + static constexpr size_t DefaultTcpInitialReceiveBufferSize = 64_KB; + static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 256_KB; + static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 256_KB; + static constexpr size_t DefaultUdpSendBufferSize = 9_KB; + static constexpr size_t DefaultUdpReceiveBufferSize = 42240; + static constexpr auto DefaultSocketBufferEfficiency = 2; + static constexpr auto DefaultConcurrency = 8; + static constexpr size_t DefaultAllocatorPoolSize = 128_KB; + + static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax)); + constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax)); + + return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + + static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize); + constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize); + + return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + public: + constexpr SystemConfigDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency) + : Config(mp, mp_sz, ap, + DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize, + DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax, + DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize, + DefaultSocketBufferEfficiency, c) + { + /* Mark as system. */ + m_system = true; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index c8bbb9ddf..08faab3ba 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -79,6 +79,19 @@ namespace ams::socket { Pf_Max = Af_Max }; + enum class MsgFlag : s32 { + MsgFlag_None = (0 << 0), + /* ... */ + MsgFlag_WaitAll = (1 << 6), + /* ... */ + }; + + enum class ShutdownMethod : u32 { + Shut_Rd = 0, + Shut_Wr = 1, + Shut_RdWr = 2, + }; + struct HostEnt { char *h_name; char **h_aliases; @@ -98,7 +111,7 @@ namespace ams::socket { Ai_NumericHost = (1 << 2), Ai_NumericServ = (1 << 3), - Ai_AddrConfig = (1 << 10), + Ai_AddrConfig = (1 << 10), }; struct SockAddr { diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp index 506290b39..f38a0d1ec 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp @@ -78,6 +78,10 @@ namespace ams::spl { return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::DisableProgramVerification); } + inline bool IsUsb30ForceEnabled() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::ExosphereForceEnableUsb30); + } + Result SetBootReason(BootReasonValue boot_reason); Result GetBootReason(BootReasonValue *out); diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 3e1f2d74e..dcff9b721 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -223,26 +223,31 @@ namespace ams::spl { Package2Hash = 17, /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - ExosphereEmummcType = 65007, - ExospherePayloadAddress = 65008, + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, + ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, + ExosphereSupportedHosVersion = 65011, }; } /* Extensions to libnx spl config item enum. */ -constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); -constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); -constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); -constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); -constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); -constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); -constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); +constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); +constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); +constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); +constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); +constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); +constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereLogConfiguration = static_cast(65009); +constexpr inline SplConfigItem SplConfigItem_ExosphereForceEnableUsb30 = static_cast(65010); diff --git a/libraries/libstratosphere/include/stratosphere/tipc.hpp b/libraries/libstratosphere/include/stratosphere/tipc.hpp new file mode 100644 index 000000000..bd139cfef --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +#include +#include + +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp new file mode 100644 index 000000000..f7ce54bf9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::tipc::impl { + + template + concept HasDefaultServiceCommandProcessor = requires (T &t, const svc::ipc::MessageBuffer &message_buffer) { + { t.ProcessDefaultServiceCommand(message_buffer) } -> std::same_as; + }; + + struct SyncFunctionTraits { + public: + template + static std::tuple GetArgsImpl(R(C::*)(A...)); + }; + + template + using SyncFunctionArgsType = decltype(SyncFunctionTraits::GetArgsImpl(F)); + + #define AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + struct NAME##ArgumentsFunctionHolder { RETURN f ARGS; }; + + #define AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + using NAME##ArgumentsType = ::ams::tipc::impl::SyncFunctionArgsType<&NAME##ArgumentsFunctionHolder::f>; + + #define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \ + class CLASSNAME : public BASECLASS { \ + private: \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \ + public: \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \ + }; + + #define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + template \ + concept Is##CLASSNAME##__##NAME##Impl = requires (T &t, Args &&... args) { \ + { t.NAME(std::forward(args)...) } -> std::same_as; \ + }; \ + \ + template \ + struct Is##CLASSNAME##__##NAME##Holder : std::false_type{}; \ + \ + template requires std::same_as, CLASSNAME::NAME##ArgumentsType> \ + struct Is##CLASSNAME##__##NAME##Holder> : std::bool_constant>{}; \ + \ + template \ + static constexpr inline bool Is##CLASSNAME##__##NAME = Is##CLASSNAME##__##NAME##Holder::value; + + #define AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + Is##CLASSNAME##__##NAME && + + #define AMS_TIPC_IMPL_DEFINE_CONCEPT(CLASSNAME, CMD_MACRO) \ + CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS) \ + \ + template \ + concept Is##CLASSNAME = CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER) true; + + #define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + else if (constexpr u16 TipcCommandId = CMD_ID + 0x10; tag == TipcCommandId) { \ + return this->ProcessMethodById(impl, message_buffer, fw_ver); \ + } + + #define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + if constexpr (constexpr u16 TipcCommandId = CMD_ID + 0x10; CommandId == TipcCommandId) { \ + constexpr bool MinValid = VERSION_MIN == hos::Version_Min; \ + constexpr bool MaxValid = VERSION_MAX == hos::Version_Max; \ + if ((MinValid || VERSION_MIN <= fw_ver) && (MaxValid || fw_ver <= VERSION_MAX)) { \ + return ::ams::tipc::impl::InvokeServiceCommandImpl(impl, message_buffer); \ + } \ + } + + #define AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, BASE, CMD_MACRO) \ + namespace NAMESPACE { \ + \ + AMS_TIPC_IMPL_DEFINE_INTERFACE(BASE, INTERFACE, CMD_MACRO) \ + AMS_TIPC_IMPL_DEFINE_CONCEPT(INTERFACE, CMD_MACRO) \ + \ + } \ + \ + namespace ams::tipc::impl { \ + \ + template \ + class ImplTemplateBaseT<::NAMESPACE::INTERFACE, Base, ImplHolder, ImplGetter, Root> : public Base, public ImplHolder { \ + public: \ + template \ + constexpr explicit ImplTemplateBaseT(Args &&...args) : ImplHolder(std::forward(args)...) { /* ... */ } \ + private: \ + template \ + ALWAYS_INLINE Result ProcessDefaultMethod(ImplType *impl, const svc::ipc::MessageBuffer &message_buffer) const { \ + /* Handle a default command. */ \ + if constexpr (HasDefaultServiceCommandProcessor) { \ + return impl->ProcessDefaultServiceCommand(message_buffer); \ + } else { \ + return tipc::ResultInvalidMethod(); \ + } \ + } \ + \ + template \ + ALWAYS_INLINE Result ProcessMethodById(ImplType *impl, const svc::ipc::MessageBuffer &message_buffer, hos::Version fw_ver) const { \ + CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID) \ + \ + return this->ProcessDefaultMethod(impl, message_buffer); \ + } \ + public: \ + virtual Result ProcessRequest() override { \ + /* Get the implementation object. */ \ + auto * const impl = ImplGetter::GetImplPointer(static_cast(this)); \ + \ + /* Get the implementation type. */ \ + using ImplType = typename std::remove_reference::type; \ + static_assert(::NAMESPACE::Is##INTERFACE); \ + \ + /* Get accessor to the message buffer. */ \ + const svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer()); \ + \ + /* Get decision variables. */ \ + const auto tag = svc::ipc::MessageBuffer::MessageHeader(message_buffer).GetTag(); \ + const auto fw_ver = hos::GetVersion(); \ + \ + /* Process against the command ids. */ \ + if (false) { } \ + CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST) \ + else { \ + return this->ProcessDefaultMethod(impl, message_buffer); \ + } \ + } \ + }; \ + \ + } + + + #define AMS_TIPC_DEFINE_INTERFACE(NAMESPACE, INTERFACE, CMD_MACRO) \ + AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, ::ams::tipc::ServiceObjectBase, CMD_MACRO) + + #define AMS_TIPC_METHOD_INFO_7(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, hos::Version_Min, hos::Version_Max) + + #define AMS_TIPC_METHOD_INFO_8(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, hos::Version_Max) + + #define AMS_TIPC_METHOD_INFO_9(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \ + HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) + + #define AMS_TIPC_METHOD_INFO_X(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, FUNC, ...) FUNC + + #define AMS_TIPC_METHOD_INFO(...) \ + AMS_TIPC_METHOD_INFO_X(, ## __VA_ARGS__, AMS_TIPC_METHOD_INFO_9(__VA_ARGS__), AMS_TIPC_METHOD_INFO_8(__VA_ARGS__), AMS_TIPC_METHOD_INFO_7(__VA_ARGS__)) + +} diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp new file mode 100644 index 000000000..439824a4a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::tipc { + + struct ClientProcessId { + os::ProcessId value; + }; + static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId"); + +} + +namespace ams::tipc::impl { + + /* Machinery for filtering type lists. */ + template + struct TupleCat; + + template + struct TupleCat, std::tuple> { + using type = std::tuple; + }; + + template