• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uber / h3-java / #405

11 Aug 2024 08:49PM CUT coverage: 98.805%. Remained the same
#405

push

github

web-flow
update pom.xml to a valid email address (#145)

496 of 502 relevant lines covered (98.8%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.42
/src/main/java/com/uber/h3core/H3CoreLoader.java
1
/*
2
 * Copyright 2017-2018, 2024 Uber Technologies, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *         http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.uber.h3core;
17

18
import java.io.File;
19
import java.io.FileOutputStream;
20
import java.io.IOException;
21
import java.io.InputStream;
22
import java.io.OutputStream;
23
import java.nio.file.Files;
24
import java.nio.file.attribute.FileAttribute;
25
import java.nio.file.attribute.PosixFilePermission;
26
import java.nio.file.attribute.PosixFilePermissions;
27
import java.util.Locale;
28
import java.util.Set;
29

30
/** Extracts the native H3 core library to the local filesystem and loads it. */
31
public final class H3CoreLoader {
32
  private H3CoreLoader() {
33
    // Prevent instantiation
34
  }
35

36
  /**
37
   * Locale used when handling system strings that need to be mapped to resource names. English is
38
   * used since that locale was used when building the library.
39
   */
40
  static final Locale H3_CORE_LOCALE = Locale.ENGLISH;
1✔
41

42
  // Supported H3 architectures
43
  static final String ARCH_X64 = "x64";
44
  static final String ARCH_X86 = "x86";
45
  static final String ARCH_ARM64 = "arm64";
46

47
  private static volatile File libraryFile = null;
1✔
48

49
  /** Read all bytes from <code>in</code> and write them to <code>out</code>. */
50
  private static void copyStream(InputStream in, OutputStream out) throws IOException {
51
    byte[] buf = new byte[4096];
1✔
52

53
    int read;
54
    while ((read = in.read(buf)) != -1) {
1✔
55
      out.write(buf, 0, read);
1✔
56
    }
57
  }
1✔
58

59
  /**
60
   * Copy the resource at the given path to the file. File will be made readable, writable, and
61
   * executable.
62
   *
63
   * @param resourcePath Resource to copy
64
   * @param newH3LibFile File to write
65
   * @throws UnsatisfiedLinkError The resource path does not exist
66
   */
67
  static void copyResource(String resourcePath, File newH3LibFile) throws IOException {
68
    // Shove the resource into the file and close it
69
    try (InputStream resource = H3CoreLoader.class.getResourceAsStream(resourcePath)) {
1✔
70
      if (resource == null) {
1✔
71
        throw new UnsatisfiedLinkError(
1✔
72
            String.format("No native resource found at %s", resourcePath));
1✔
73
      }
74

75
      try (FileOutputStream outFile = new FileOutputStream(newH3LibFile)) {
1✔
76
        copyStream(resource, outFile);
1✔
77
      }
78
    }
79
  }
1✔
80

81
  /**
82
   * For use when the H3 library should be unpacked from the JAR and loaded.
83
   *
84
   * @throws SecurityException Loading the library was not allowed by the SecurityManager.
85
   * @throws UnsatisfiedLinkError The library could not be loaded
86
   * @throws IOException Failed to unpack the library
87
   */
88
  public static NativeMethods loadNatives() throws IOException {
89
    final OperatingSystem os =
1✔
90
        detectOs(System.getProperty("java.vendor"), System.getProperty("os.name"));
1✔
91
    final String arch = detectArch(System.getProperty("os.arch"));
1✔
92

93
    return loadNatives(os, arch);
1✔
94
  }
95

96
  private static File createTempLibraryFile(OperatingSystem os) throws IOException {
97
    if (os.isPosix()) {
1✔
98
      // Note this is already done by the implementation of Files.createTempFile that I looked at,
99
      // but the javadoc does not seem to gaurantee the permissions will be restricted to owner
100
      // write.
101
      final FileAttribute<Set<PosixFilePermission>> attr =
1✔
102
          PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
1✔
103
      return Files.createTempFile("libh3-java", os.getSuffix(), attr).toFile();
1✔
104
    } else {
105
      // When not a POSIX OS, try to ensure the permissions are secure
106
      final File f = Files.createTempFile("libh3-java", os.getSuffix()).toFile();
×
107
      f.setReadable(true, true);
×
108
      f.setWritable(true, true);
×
109
      f.setExecutable(true, true);
×
110
      return f;
×
111
    }
112
  }
113

114
  /**
115
   * For use when the H3 library should be unpacked from the JAR and loaded. The native library for
116
   * the specified operating system and architecture will be extract.
117
   *
118
   * <p>H3 will only successfully extract the library once, even if different operating system and
119
   * architecture are specified, or if {@link #loadNatives()} was used instead.
120
   *
121
   * @throws SecurityException Loading the library was not allowed by the SecurityManager.
122
   * @throws UnsatisfiedLinkError The library could not be loaded
123
   * @throws IOException Failed to unpack the library
124
   * @param os Operating system whose lobrary should be used
125
   * @param arch Architecture name, as packaged in the H3 library
126
   */
127
  public static synchronized NativeMethods loadNatives(OperatingSystem os, String arch)
128
      throws IOException {
129
    // This is synchronized because if multiple threads were writing and
130
    // loading the shared object at the same time, bad things could happen.
131

132
    if (libraryFile == null) {
1✔
133
      final String dirName = String.format("%s-%s", os.getDirName(), arch);
1✔
134
      final String libName = String.format("libh3-java%s", os.getSuffix());
1✔
135

136
      final File newLibraryFile = createTempLibraryFile(os);
1✔
137
      newLibraryFile.deleteOnExit();
1✔
138

139
      copyResource(String.format("/%s/%s", dirName, libName), newLibraryFile);
1✔
140

141
      System.load(newLibraryFile.getCanonicalPath());
1✔
142

143
      libraryFile = newLibraryFile;
1✔
144
    }
145

146
    return new NativeMethods();
1✔
147
  }
148

149
  /**
150
   * For use when the H3 library is installed system-wide and Java is able to locate it.
151
   *
152
   * @throws SecurityException Loading the library was not allowed by the SecurityManager.
153
   * @throws UnsatisfiedLinkError The library could not be loaded
154
   */
155
  public static NativeMethods loadSystemNatives() {
156
    System.loadLibrary("h3-java");
1✔
157

158
    return new NativeMethods();
1✔
159
  }
160

161
  /** Operating systems supported by H3-Java. */
162
  public enum OperatingSystem {
1✔
163
    ANDROID(".so"),
1✔
164
    DARWIN(".dylib"),
1✔
165
    FREEBSD(".so"),
1✔
166
    WINDOWS(".dll", false),
1✔
167
    LINUX(".so");
1✔
168

169
    private final String suffix;
170
    private final boolean posix;
171

172
    OperatingSystem(String suffix) {
1✔
173
      this.suffix = suffix;
1✔
174
      this.posix = true;
1✔
175
    }
1✔
176

177
    OperatingSystem(String suffix, boolean posix) {
1✔
178
      this.suffix = suffix;
1✔
179
      this.posix = posix;
1✔
180
    }
1✔
181

182
    /** Suffix for native libraries. */
183
    public String getSuffix() {
184
      return suffix;
1✔
185
    }
186

187
    /** How this operating system's name is rendered when extracting the native library. */
188
    public String getDirName() {
189
      return name().toLowerCase(H3_CORE_LOCALE);
1✔
190
    }
191

192
    /** Whether to try to use POSIX file permissions when creating the native library temp file. */
193
    public boolean isPosix() {
194
      return this.posix;
1✔
195
    }
196
  }
197

198
  /**
199
   * Detect the current operating system.
200
   *
201
   * @param javaVendor Value of system property "java.vendor"
202
   * @param osName Value of system property "os.name"
203
   */
204
  static final OperatingSystem detectOs(String javaVendor, String osName) {
205
    // Detecting Android using the properties from:
206
    // https://developer.android.com/reference/java/lang/System.html
207
    if (javaVendor.toLowerCase(H3_CORE_LOCALE).contains("android")) {
1✔
208
      return OperatingSystem.ANDROID;
1✔
209
    }
210

211
    String javaOs = osName.toLowerCase(H3_CORE_LOCALE);
1✔
212
    if (javaOs.contains("mac")) {
1✔
213
      return OperatingSystem.DARWIN;
1✔
214
    } else if (javaOs.contains("win")) {
1✔
215
      return OperatingSystem.WINDOWS;
1✔
216
    } else if (javaOs.contains("freebsd")) {
1✔
217
      return OperatingSystem.FREEBSD;
1✔
218
    } else {
219
      // Only other supported platform
220
      return OperatingSystem.LINUX;
1✔
221
    }
222
  }
223

224
  /**
225
   * Detect the system architecture.
226
   *
227
   * @param osArch Value of system property "os.arch"
228
   */
229
  static final String detectArch(String osArch) {
230
    if (osArch.equals("amd64") || osArch.equals("x86_64")) {
1✔
231
      return ARCH_X64;
1✔
232
    } else if (osArch.equals("i386")
1✔
233
        || osArch.equals("i486")
1✔
234
        || osArch.equals("i586")
1✔
235
        || osArch.equals("i686")
1✔
236
        || osArch.equals("i786")
1✔
237
        || osArch.equals("i886")) {
1✔
238
      return ARCH_X86;
1✔
239
    } else if (osArch.equals("aarch64")) {
1✔
240
      return ARCH_ARM64;
1✔
241
    } else {
242
      return osArch;
1✔
243
    }
244
  }
245
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc