From eadfaa07422286ca0aecc2baf289504a432411b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nemanja=20Zbilji=C4=87?= Date: Sun, 25 Sep 2016 18:07:29 +0200 Subject: [PATCH 1/6] Support OSXFUSE 3.x, fall back to 2.x if needed Also supports custom bundled OSXFUSE installs. --- mount_config.go | 43 ++++++++++++++++++++++++ mount_darwin.go | 87 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 103 insertions(+), 27 deletions(-) diff --git a/mount_config.go b/mount_config.go index 6828ed3..45d9546 100644 --- a/mount_config.go +++ b/mount_config.go @@ -135,6 +135,14 @@ type MountConfig struct { // default name involving the string 'osxfuse' is used. VolumeName string + // OS X only. + // + // OSXFUSELocations sets where to look for OSXFUSE files. The arguments are + // all the possible locations. The previous locations are replaced. + // + // Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are used. + OSXFUSELocations []OSXFUSEPaths + // Additional key=value options to pass unadulterated to the underlying mount // command. See `man 8 mount`, the fuse documentation, etc. for // system-specific information. @@ -230,3 +238,38 @@ func (c *MountConfig) toOptionsString() string { return strings.Join(components, ",") } + +// OSXFUSEPaths describes the paths used by an installed OSXFUSE version. +// See OSXFUSELocationV3 for typical values. +type OSXFUSEPaths struct { + // Prefix for the device file. At mount time, an incrementing number is + // suffixed until a free FUSE device is found. + DevicePrefix string + + // Path of the load helper, used to load the kernel extension if no device + // files are found. + Load string + + // Path of the mount helper, used for the actual mount operation. + Mount string + + // Environment variable used to pass the path to the executable calling the + // mount helper. + DaemonVar string +} + +// Default paths for OSXFUSE. See OSXFUSELocations. +var ( + OSXFUSELocationV3 = OSXFUSEPaths{ + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", + Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", + DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", + } + OSXFUSELocationV2 = OSXFUSEPaths{ + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", + Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", + DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + } +) diff --git a/mount_darwin.go b/mount_darwin.go index 8ecf640..97dc1a1 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -14,10 +14,16 @@ import ( ) var errNoAvail = errors.New("no available fuse devices") -var errNotLoaded = errors.New("osxfusefs is not loaded") +var errNotLoaded = errors.New("osxfuse is not loaded") -func loadOSXFUSE() error { - cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs") +// errOSXFUSENotFound is returned from Mount when the OSXFUSE installation is +// not detected. +// +// Make sure OSXFUSE is installed, or see OSXFUSELocations for customization. +var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE") + +func loadOSXFUSE(bin string) error { + cmd := exec.Command(bin) cmd.Dir = "/" cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -25,10 +31,10 @@ func loadOSXFUSE() error { return err } -func openOSXFUSEDev() (dev *os.File, err error) { +func openOSXFUSEDev(devPrefix string) (dev *os.File, err error) { // Try each device name. for i := uint64(0); ; i++ { - path := fmt.Sprintf("/dev/osxfuse%d", i) + path := devPrefix + strconv.FormatUint(i, 10) dev, err = os.OpenFile(path, os.O_RDWR, 0000) if os.IsNotExist(err) { if i == 0 { @@ -52,11 +58,12 @@ func openOSXFUSEDev() (dev *os.File, err error) { } func callMount( + bin string, + daemonVar string, dir string, cfg *MountConfig, dev *os.File, ready chan<- error) (err error) { - const bin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs" // The mount helper doesn't understand any escaping. for k, v := range cfg.toMap() { @@ -85,8 +92,15 @@ func callMount( ) cmd.ExtraFiles = []*os.File{dev} cmd.Env = os.Environ() + // OSXFUSE <3.3.0 cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=") - cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin) + // OSXFUSE >=3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=") + + daemon := os.Args[0] + if daemonVar != "" { + cmd.Env = append(cmd.Env, daemonVar+"="+daemon) + } var buf bytes.Buffer cmd.Stdout = &buf @@ -122,34 +136,53 @@ func mount( dir string, cfg *MountConfig, ready chan<- error) (dev *os.File, err error) { - // Open the device. - dev, err = openOSXFUSEDev() + // get OSXFUSE locations + locations := cfg.OSXFUSELocations + if locations == nil { + locations = []OSXFUSEPaths{ + OSXFUSELocationV3, + OSXFUSELocationV2, + } + } - // Special case: we may need to explicitly load osxfuse. Load it, then try - // again. - if err == errNotLoaded { - err = loadOSXFUSE() + for _, loc := range locations { + if _, err := os.Stat(loc.Mount); os.IsNotExist(err) { + // try the other locations + continue + } + + // Open the device. + dev, err = openOSXFUSEDev(loc.DevicePrefix) + + // Special case: we may need to explicitly load osxfuse. Load it, then + // try again. + if err == errNotLoaded { + err = loadOSXFUSE(loc.Load) + if err != nil { + err = fmt.Errorf("loadOSXFUSE: %v", err) + return + } + + dev, err = openOSXFUSEDev(loc.DevicePrefix) + } + + // Propagate errors. if err != nil { - err = fmt.Errorf("loadOSXFUSE: %v", err) + err = fmt.Errorf("openOSXFUSEDev: %v", err) return } - dev, err = openOSXFUSEDev() - } + // Call the mount binary with the device. + err = callMount(loc.Mount, loc.DaemonVar, dir, cfg, dev, ready) + if err != nil { + dev.Close() + err = fmt.Errorf("callMount: %v", err) + return + } - // Propagate errors. - if err != nil { - err = fmt.Errorf("openOSXFUSEDev: %v", err) - return - } - - // Call the mount binary with the device. - err = callMount(dir, cfg, dev, ready) - if err != nil { - dev.Close() - err = fmt.Errorf("callMount: %v", err) return } + err = errOSXFUSENotFound return } From fc5bd98d6508fd4bf6411389677bae9ca5cd7e3f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 16 Oct 2016 08:56:34 +0100 Subject: [PATCH 2/6] Don't allow configurability for osxfuse paths. There's no need to complicate the API. If osxfuse makes a backwards-incompatible change again, we can add support for it. --- mount_config.go | 36 ++++++++++++++++-------------------- mount_darwin.go | 16 +++------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/mount_config.go b/mount_config.go index 45d9546..ac3c1f3 100644 --- a/mount_config.go +++ b/mount_config.go @@ -135,14 +135,6 @@ type MountConfig struct { // default name involving the string 'osxfuse' is used. VolumeName string - // OS X only. - // - // OSXFUSELocations sets where to look for OSXFUSE files. The arguments are - // all the possible locations. The previous locations are replaced. - // - // Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are used. - OSXFUSELocations []OSXFUSEPaths - // Additional key=value options to pass unadulterated to the underlying mount // command. See `man 8 mount`, the fuse documentation, etc. for // system-specific information. @@ -258,18 +250,22 @@ type OSXFUSEPaths struct { DaemonVar string } -// Default paths for OSXFUSE. See OSXFUSELocations. var ( - OSXFUSELocationV3 = OSXFUSEPaths{ - DevicePrefix: "/dev/osxfuse", - Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", - Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", - DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", - } - OSXFUSELocationV2 = OSXFUSEPaths{ - DevicePrefix: "/dev/osxfuse", - Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", - Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", - DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + osxfuseLocations = []OSXFUSEPaths{ + // v3 + { + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", + Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", + DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", + }, + + // v2 + { + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", + Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", + DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + }, } ) diff --git a/mount_darwin.go b/mount_darwin.go index 97dc1a1..7bff85c 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -17,9 +17,7 @@ var errNoAvail = errors.New("no available fuse devices") var errNotLoaded = errors.New("osxfuse is not loaded") // errOSXFUSENotFound is returned from Mount when the OSXFUSE installation is -// not detected. -// -// Make sure OSXFUSE is installed, or see OSXFUSELocations for customization. +// not detected. Make sure OSXFUSE is installed. var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE") func loadOSXFUSE(bin string) error { @@ -136,16 +134,8 @@ func mount( dir string, cfg *MountConfig, ready chan<- error) (dev *os.File, err error) { - // get OSXFUSE locations - locations := cfg.OSXFUSELocations - if locations == nil { - locations = []OSXFUSEPaths{ - OSXFUSELocationV3, - OSXFUSELocationV2, - } - } - - for _, loc := range locations { + // Find the version of osxfuse installed on this machine. + for _, loc := range osxfuseLocations { if _, err := os.Stat(loc.Mount); os.IsNotExist(err) { // try the other locations continue From f1e482d1552e30cfdd5d1f8d43fe956e43807c96 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 16 Oct 2016 08:56:40 +0100 Subject: [PATCH 3/6] Move osxfuse-specific config into mount_darwin.go. --- mount_config.go | 39 --------------------------------------- mount_darwin.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/mount_config.go b/mount_config.go index ac3c1f3..6828ed3 100644 --- a/mount_config.go +++ b/mount_config.go @@ -230,42 +230,3 @@ func (c *MountConfig) toOptionsString() string { return strings.Join(components, ",") } - -// OSXFUSEPaths describes the paths used by an installed OSXFUSE version. -// See OSXFUSELocationV3 for typical values. -type OSXFUSEPaths struct { - // Prefix for the device file. At mount time, an incrementing number is - // suffixed until a free FUSE device is found. - DevicePrefix string - - // Path of the load helper, used to load the kernel extension if no device - // files are found. - Load string - - // Path of the mount helper, used for the actual mount operation. - Mount string - - // Environment variable used to pass the path to the executable calling the - // mount helper. - DaemonVar string -} - -var ( - osxfuseLocations = []OSXFUSEPaths{ - // v3 - { - DevicePrefix: "/dev/osxfuse", - Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", - Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", - DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", - }, - - // v2 - { - DevicePrefix: "/dev/osxfuse", - Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", - Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", - DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", - }, - } -) diff --git a/mount_darwin.go b/mount_darwin.go index 7bff85c..81fb3e1 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -20,6 +20,45 @@ var errNotLoaded = errors.New("osxfuse is not loaded") // not detected. Make sure OSXFUSE is installed. var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE") +// OSXFUSEPaths describes the paths used by an installed OSXFUSE version. +// See OSXFUSELocationV3 for typical values. +type OSXFUSEPaths struct { + // Prefix for the device file. At mount time, an incrementing number is + // suffixed until a free FUSE device is found. + DevicePrefix string + + // Path of the load helper, used to load the kernel extension if no device + // files are found. + Load string + + // Path of the mount helper, used for the actual mount operation. + Mount string + + // Environment variable used to pass the path to the executable calling the + // mount helper. + DaemonVar string +} + +var ( + osxfuseLocations = []OSXFUSEPaths{ + // v3 + { + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", + Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", + DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", + }, + + // v2 + { + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", + Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", + DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + }, + } +) + func loadOSXFUSE(bin string) error { cmd := exec.Command(bin) cmd.Dir = "/" From 8b5caee5750dff673f51f5262438b509bddc90ce Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 16 Oct 2016 08:56:44 +0100 Subject: [PATCH 4/6] Unexport OSXFUSEPaths. --- mount_darwin.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mount_darwin.go b/mount_darwin.go index 81fb3e1..d3dbbb9 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -20,9 +20,9 @@ var errNotLoaded = errors.New("osxfuse is not loaded") // not detected. Make sure OSXFUSE is installed. var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE") -// OSXFUSEPaths describes the paths used by an installed OSXFUSE version. -// See OSXFUSELocationV3 for typical values. -type OSXFUSEPaths struct { +// osxfuseInstallation describes the paths used by an installed OSXFUSE +// version. +type osxfuseInstallation struct { // Prefix for the device file. At mount time, an incrementing number is // suffixed until a free FUSE device is found. DevicePrefix string @@ -40,7 +40,7 @@ type OSXFUSEPaths struct { } var ( - osxfuseLocations = []OSXFUSEPaths{ + osxfuseInstallations = []osxfuseInstallation{ // v3 { DevicePrefix: "/dev/osxfuse", @@ -174,7 +174,7 @@ func mount( cfg *MountConfig, ready chan<- error) (dev *os.File, err error) { // Find the version of osxfuse installed on this machine. - for _, loc := range osxfuseLocations { + for _, loc := range osxfuseInstallations { if _, err := os.Stat(loc.Mount); os.IsNotExist(err) { // try the other locations continue From b2c339638210806ba335a91031c340c7873c0611 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 16 Oct 2016 08:56:51 +0100 Subject: [PATCH 5/6] Update state around "close without msyc" behavior on OS X. --- fuseops/ops.go | 5 +++-- samples/flushfs/flush_fs_test.go | 12 +++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index b26d989..14d5411 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -696,9 +696,10 @@ type SyncFileOp struct { // * On OS X, if a user modifies a mapped file via the mapping before // closing the file with close(2), the WriteFileOps for the modifications // may not be received before the FlushFileOp for the close(2) (cf. -// http://goo.gl/kVmNcx). +// https://github.com/osxfuse/osxfuse/issues/202). It appears that this may +// be fixed in osxfuse 3 (cf. https://goo.gl/rtvbko). // -// * However, even on OS X you can arrange for writes via a mapping to be +// * However, you safely can arrange for writes via a mapping to be // flushed by calling msync(2) followed by close(2). On OS X msync(2) // will cause a WriteFileOps to go through and close(2) will cause a // FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does diff --git a/samples/flushfs/flush_fs_test.go b/samples/flushfs/flush_fs_test.go index e787998..8b5d9aa 100644 --- a/samples/flushfs/flush_fs_test.go +++ b/samples/flushfs/flush_fs_test.go @@ -605,19 +605,13 @@ func (t *NoErrorsTest) Mmap_NoMsync_MunmapBeforeClose() { ExpectThat(t.getFlushes(), ElementsAre()) ExpectThat(t.getFsyncs(), ElementsAre()) - // Close the file. We should see a flush. On Darwin, this will contain out of - // date contents (cf. https://github.com/osxfuse/osxfuse/issues/202). + // Close the file. We should see a flush with up to date contents. err = t.f1.Close() t.f1 = nil AssertEq(nil, err) - if isDarwin { - ExpectThat(t.getFlushes(), ElementsAre("taco")) - ExpectThat(t.getFsyncs(), ElementsAre()) - } else { - ExpectThat(t.getFlushes(), ElementsAre("paco")) - ExpectThat(t.getFsyncs(), ElementsAre()) - } + ExpectThat(t.getFlushes(), ElementsAre("paco")) + ExpectThat(t.getFsyncs(), ElementsAre()) } func (t *NoErrorsTest) Mmap_NoMsync_CloseBeforeMunmap() { From a067b22c1da41e75df9a03424c3af506d6d2e018 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Sun, 16 Oct 2016 09:12:42 +0100 Subject: [PATCH 6/6] Fix issues around statfs(2) changes with osxfuse 3. Tested on OS X 10.11.6 (15G1004) with osxfuse 3.5.2. --- fuseops/ops.go | 4 +- samples/statfs/statfs_darwin_test.go | 72 ++++++++++++++++------------ 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 14d5411..609249f 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -59,7 +59,7 @@ type StatFSOp struct { // // On Linux this can be any value, and will be faithfully returned to the // caller of statfs(2) (see the code walk above). On OS X it appears that - // only powers of 2 in the range [2^9, 2^17] are preserved, and a value of + // only powers of 2 in the range [2^7, 2^20] are preserved, and a value of // zero is treated as 4096. // // This interface does not distinguish between blocks and block fragments. @@ -85,7 +85,7 @@ type StatFSOp struct { // transfer block size". // // On Linux this can be any value. On OS X it appears that only powers of 2 - // in the range [2^12, 2^20] are faithfully preserved, and a value of zero is + // in the range [2^12, 2^25] are faithfully preserved, and a value of zero is // treated as 65536. IoSize uint32 diff --git a/samples/statfs/statfs_darwin_test.go b/samples/statfs/statfs_darwin_test.go index b19600e..9845fc4 100644 --- a/samples/statfs/statfs_darwin_test.go +++ b/samples/statfs/statfs_darwin_test.go @@ -70,7 +70,7 @@ func (t *StatFSTest) Syscall_ZeroValues() { ExpectEq(0, stat.Bavail) ExpectEq(0, stat.Files) ExpectEq(0, stat.Ffree) - ExpectEq("osxfusefs", convertName(stat.Fstypename[:])) + ExpectEq("osxfuse", convertName(stat.Fstypename[:])) ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:])) ExpectEq(fsName, convertName(stat.Mntfromname[:])) } @@ -105,7 +105,7 @@ func (t *StatFSTest) Syscall_NonZeroValues() { ExpectEq(canned.BlocksAvailable, stat.Bavail) ExpectEq(canned.Inodes, stat.Files) ExpectEq(canned.InodesFree, stat.Ffree) - ExpectEq("osxfusefs", convertName(stat.Fstypename[:])) + ExpectEq("osxfuse", convertName(stat.Fstypename[:])) ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:])) ExpectEq(fsName, convertName(stat.Mntfromname[:])) } @@ -120,8 +120,8 @@ func (t *StatFSTest) BlockSizes() { expectedBsize uint32 }{ 0: {0, 4096}, - 1: {1, 512}, - 2: {3, 512}, + 1: {1, 128}, + 2: {3, 128}, 3: {511, 512}, 4: {512, 512}, 5: {513, 1024}, @@ -129,16 +129,22 @@ func (t *StatFSTest) BlockSizes() { 7: {1024, 1024}, 8: {4095, 4096}, 9: {1 << 16, 1 << 16}, - 10: {1<<17 - 1, 1 << 17}, - 11: {1 << 17, 1 << 17}, - 12: {1<<17 + 1, 1 << 17}, - 13: {1 << 18, 1 << 17}, - 14: {1 << 20, 1 << 17}, - 15: {math.MaxInt32 - 1, 1 << 17}, - 16: {math.MaxInt32, 1 << 17}, - 17: {math.MaxInt32 + 1, 512}, - 18: {math.MaxInt32 + 1<<15, 1 << 15}, - 19: {math.MaxUint32, 1 << 17}, + 10: {1 << 17, 1 << 17}, + 11: {1 << 18, 1 << 18}, + 12: {1 << 19, 1 << 19}, + + 13: {1<<20 - 1, 1 << 20}, + 14: {1 << 20, 1 << 20}, + 15: {1<<20 + 1, 1 << 20}, + + 16: {1 << 21, 1 << 20}, + 17: {1 << 22, 1 << 20}, + + 18: {math.MaxInt32 - 1, 1 << 20}, + 19: {math.MaxInt32, 1 << 20}, + 20: {math.MaxInt32 + 1, 128}, + 21: {math.MaxInt32 + 1<<15, 1 << 15}, + 22: {math.MaxUint32, 1 << 20}, } for i, tc := range testCases { @@ -170,23 +176,29 @@ func (t *StatFSTest) IoSizes() { fsIoSize uint32 expectedIosize uint32 }{ - 0: {0, 65536}, - 1: {1, 4096}, - 2: {3, 4096}, - 3: {4095, 4096}, - 4: {4096, 4096}, - 5: {4097, 8192}, - 6: {8191, 8192}, - 7: {8192, 8192}, - 8: {8193, 16384}, - 9: {1<<20 - 1, 1 << 20}, + 0: {0, 65536}, + 1: {1, 4096}, + 2: {3, 4096}, + 3: {4095, 4096}, + 4: {4096, 4096}, + 5: {4097, 8192}, + 6: {8191, 8192}, + 7: {8192, 8192}, + 8: {8193, 16384}, + + 9: {1 << 18, 1 << 18}, 10: {1 << 20, 1 << 20}, - 11: {1<<20 + 1, 1 << 20}, - 12: {math.MaxInt32 - 1, 1 << 20}, - 13: {math.MaxInt32, 1 << 20}, - 14: {math.MaxInt32 + 1, 4096}, - 15: {math.MaxInt32 + 1<<15, 1 << 15}, - 16: {math.MaxUint32, 1 << 20}, + 11: {1 << 23, 1 << 23}, + + 12: {1<<25 - 1, 1 << 25}, + 13: {1 << 25, 1 << 25}, + 14: {1<<25 + 1, 1 << 25}, + + 15: {math.MaxInt32 - 1, 1 << 25}, + 16: {math.MaxInt32, 1 << 25}, + 17: {math.MaxInt32 + 1, 4096}, + 18: {math.MaxInt32 + 1<<15, 1 << 15}, + 19: {math.MaxUint32, 1 << 25}, } for i, tc := range testCases {