提交 9e9d4238 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Forbid non-finite values in GEOMETRY, they are illegal in WKT

上级 78018191
...@@ -15,6 +15,8 @@ import static org.h2.util.geometry.GeometryUtils.MULTI_POINT; ...@@ -15,6 +15,8 @@ import static org.h2.util.geometry.GeometryUtils.MULTI_POINT;
import static org.h2.util.geometry.GeometryUtils.MULTI_POLYGON; import static org.h2.util.geometry.GeometryUtils.MULTI_POLYGON;
import static org.h2.util.geometry.GeometryUtils.POINT; import static org.h2.util.geometry.GeometryUtils.POINT;
import static org.h2.util.geometry.GeometryUtils.POLYGON; import static org.h2.util.geometry.GeometryUtils.POLYGON;
import static org.h2.util.geometry.GeometryUtils.checkFinite;
import static org.h2.util.geometry.GeometryUtils.toCanonicalDouble;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
...@@ -39,6 +41,8 @@ public final class EWKBUtils { ...@@ -39,6 +41,8 @@ public final class EWKBUtils {
private final byte[] buf = new byte[8]; private final byte[] buf = new byte[8];
private int type;
private int level; private int level;
/** /**
...@@ -84,6 +88,7 @@ public final class EWKBUtils { ...@@ -84,6 +88,7 @@ public final class EWKBUtils {
} }
private void writeHeader(int type, int srid) { private void writeHeader(int type, int srid) {
this.type = type;
switch (dimensionSystem) { switch (dimensionSystem) {
case DIMENSION_SYSTEM_XYZ: case DIMENSION_SYSTEM_XYZ:
type |= EWKB_Z; type |= EWKB_Z;
...@@ -120,13 +125,19 @@ public final class EWKBUtils { ...@@ -120,13 +125,19 @@ public final class EWKBUtils {
@Override @Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) { protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
boolean check = type != POINT || !Double.isNaN(x) || !Double.isNaN(y) || !Double.isNaN(z)
|| !Double.isNaN(m);
if (check) {
checkFinite(x);
checkFinite(y);
}
writeDouble(x); writeDouble(x);
writeDouble(y); writeDouble(y);
if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) { if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) {
writeDouble(z); writeDouble(check ? checkFinite(z) : z);
} }
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) { if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
writeDouble(m); writeDouble(check ? checkFinite(m) : m);
} }
} }
...@@ -434,18 +445,6 @@ public final class EWKBUtils { ...@@ -434,18 +445,6 @@ public final class EWKBUtils {
index, total); index, total);
} }
/**
* Normalizes all NaNs into single type on NaN and negative zero to positive
* zero.
*
* @param d
* double value
* @return normalized value
*/
static double toCanonicalDouble(double d) {
return Double.isNaN(d) ? Double.NaN : d == 0d ? 0d : d;
}
private EWKBUtils() { private EWKBUtils() {
} }
......
...@@ -206,7 +206,7 @@ public final class EWKTUtils { ...@@ -206,7 +206,7 @@ public final class EWKTUtils {
} }
private void writeDouble(double v) { private void writeDouble(double v) {
String s = Double.toString(v); String s = Double.toString(GeometryUtils.checkFinite(v));
if (s.endsWith(".0")) { if (s.endsWith(".0")) {
output.append(s, 0, s.length() - 2); output.append(s, 0, s.length() - 2);
} else { } else {
...@@ -354,8 +354,6 @@ public final class EWKTUtils { ...@@ -354,8 +354,6 @@ public final class EWKTUtils {
case '+': case '+':
case '-': case '-':
case '.': case '.':
case 'N':
case 'n':
return true; return true;
default: default:
return false; return false;
...@@ -370,12 +368,8 @@ public final class EWKTUtils { ...@@ -370,12 +368,8 @@ public final class EWKTUtils {
case '+': case '+':
case '-': case '-':
case '.': case '.':
case 'A':
case 'E': case 'E':
case 'N':
case 'a':
case 'e': case 'e':
case 'n':
return true; return true;
default: default:
return false; return false;
......
...@@ -191,6 +191,7 @@ public final class GeometryUtils { ...@@ -191,6 +191,7 @@ public final class GeometryUtils {
@Override @Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) { protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
// POINT EMPTY has NaNs
if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) { if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) { if (!set) {
minX = maxX = x; minX = maxX = x;
...@@ -318,6 +319,7 @@ public final class GeometryUtils { ...@@ -318,6 +319,7 @@ public final class GeometryUtils {
if (!hasM && !Double.isNaN(m)) { if (!hasM && !Double.isNaN(m)) {
hasM = true; hasM = true;
} }
// POINT EMPTY has NaNs
if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) { if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) { if (!set) {
minX = maxX = x; minX = maxX = x;
...@@ -584,6 +586,14 @@ public final class GeometryUtils { ...@@ -584,6 +586,14 @@ public final class GeometryUtils {
return Double.isNaN(d) ? Double.NaN : d == 0d ? 0d : d; return Double.isNaN(d) ? Double.NaN : d == 0d ? 0d : d;
} }
static double checkFinite(double d) {
// Do not push this negation down, it will break NaN rejection
if (!(Math.abs(d) <= Double.MAX_VALUE)) {
throw new IllegalArgumentException();
}
return d;
}
private GeometryUtils() { private GeometryUtils() {
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.util.geometry; package org.h2.util.geometry;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM; import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZ;
import static org.h2.util.geometry.GeometryUtils.GEOMETRY_COLLECTION; import static org.h2.util.geometry.GeometryUtils.GEOMETRY_COLLECTION;
import static org.h2.util.geometry.GeometryUtils.LINE_STRING; import static org.h2.util.geometry.GeometryUtils.LINE_STRING;
import static org.h2.util.geometry.GeometryUtils.M; import static org.h2.util.geometry.GeometryUtils.M;
...@@ -17,6 +18,7 @@ import static org.h2.util.geometry.GeometryUtils.POLYGON; ...@@ -17,6 +18,7 @@ import static org.h2.util.geometry.GeometryUtils.POLYGON;
import static org.h2.util.geometry.GeometryUtils.X; import static org.h2.util.geometry.GeometryUtils.X;
import static org.h2.util.geometry.GeometryUtils.Y; import static org.h2.util.geometry.GeometryUtils.Y;
import static org.h2.util.geometry.GeometryUtils.Z; import static org.h2.util.geometry.GeometryUtils.Z;
import static org.h2.util.geometry.GeometryUtils.checkFinite;
import static org.h2.util.geometry.GeometryUtils.toCanonicalDouble; import static org.h2.util.geometry.GeometryUtils.toCanonicalDouble;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
...@@ -154,11 +156,12 @@ public final class JTSUtils { ...@@ -154,11 +156,12 @@ public final class JTSUtils {
return; return;
} }
CoordinateSequence coordinates = innerOffset < 0 ? this.coordinates : innerCoordinates[innerOffset]; CoordinateSequence coordinates = innerOffset < 0 ? this.coordinates : innerCoordinates[innerOffset];
coordinates.setOrdinate(index, X, x); coordinates.setOrdinate(index, X, checkFinite(x));
coordinates.setOrdinate(index, Y, y); coordinates.setOrdinate(index, Y, checkFinite(y));
coordinates.setOrdinate(index, Z, z); coordinates.setOrdinate(index, Z,
(dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0 ? checkFinite(z) : Double.NaN);
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) { if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
coordinates.setOrdinate(index, M, m); coordinates.setOrdinate(index, M, checkFinite(m));
} }
} }
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XY;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZ;
import static org.h2.util.geometry.GeometryUtils.M; import static org.h2.util.geometry.GeometryUtils.M;
import static org.h2.util.geometry.GeometryUtils.MAX_X; import static org.h2.util.geometry.GeometryUtils.MAX_X;
import static org.h2.util.geometry.GeometryUtils.MAX_Y; import static org.h2.util.geometry.GeometryUtils.MAX_Y;
...@@ -14,16 +17,21 @@ import static org.h2.util.geometry.GeometryUtils.X; ...@@ -14,16 +17,21 @@ import static org.h2.util.geometry.GeometryUtils.X;
import static org.h2.util.geometry.GeometryUtils.Y; import static org.h2.util.geometry.GeometryUtils.Y;
import static org.h2.util.geometry.GeometryUtils.Z; import static org.h2.util.geometry.GeometryUtils.Z;
import java.io.ByteArrayOutputStream;
import java.util.Random; import java.util.Random;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.geometry.EWKBUtils; import org.h2.util.geometry.EWKBUtils;
import org.h2.util.geometry.EWKBUtils.EWKBTarget;
import org.h2.util.geometry.EWKTUtils; import org.h2.util.geometry.EWKTUtils;
import org.h2.util.geometry.EWKTUtils.EWKTTarget;
import org.h2.util.geometry.GeometryUtils; import org.h2.util.geometry.GeometryUtils;
import org.h2.util.geometry.GeometryUtils.DimensionSystemTarget; import org.h2.util.geometry.GeometryUtils.DimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget; import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.Target;
import org.h2.util.geometry.JTSUtils; import org.h2.util.geometry.JTSUtils;
import org.h2.util.geometry.JTSUtils.GeometryTarget;
import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
...@@ -39,6 +47,29 @@ import org.locationtech.jts.io.WKTWriter; ...@@ -39,6 +47,29 @@ import org.locationtech.jts.io.WKTWriter;
*/ */
public class TestGeometryUtils extends TestBase { public class TestGeometryUtils extends TestBase {
private static final byte[][] NON_FINITE = { //
// XY
StringUtils.convertHexToBytes("0000000001" //
+ "0000000000000000" //
+ "7ff8000000000000"), //
// XY
StringUtils.convertHexToBytes("0000000001" //
+ "7ff8000000000000" //
+ "0000000000000000"), //
// XYZ
StringUtils.convertHexToBytes("0080000001" //
+ "0000000000000000" //
+ "0000000000000000" //
+ "7ff8000000000000"), //
// XYM
StringUtils.convertHexToBytes("0040000001" //
+ "0000000000000000" //
+ "0000000000000000" //
+ "7ff8000000000000") };
private static final int[] NON_FINITE_DIMENSIONS = { DIMENSION_SYSTEM_XY, DIMENSION_SYSTEM_XY, DIMENSION_SYSTEM_XYZ,
DIMENSION_SYSTEM_XYM };
/** /**
* Run just this test. * Run just this test.
* *
...@@ -61,14 +92,16 @@ public class TestGeometryUtils extends TestBase { ...@@ -61,14 +92,16 @@ public class TestGeometryUtils extends TestBase {
testEmptyPoint(); testEmptyPoint();
testDimensionM(); testDimensionM();
testDimensionZM(); testDimensionZM();
testFiniteOnly();
testSRID(); testSRID();
testIntersectionAndUnion(); testIntersectionAndUnion();
} }
private void testPoint() throws Exception { private void testPoint() throws Exception {
testGeometry("POINT (1 2)", 2); testGeometry("POINT (1 2)", 2);
testGeometry("POINT (-1.3 NaN)", 2); testGeometry("POINT (-1.3 15)", 2);
testGeometry("POINT (-1E32 NaN)", "POINT (-1E32 NaN)", "POINT (-100000000000000000000000000000000 NaN)", 2); testGeometry("POINT (-1E32 1.000001)", "POINT (-1E32 1.000001)",
"POINT (-100000000000000000000000000000000 1.000001)", 2);
testGeometry("POINT Z (2.7 -3 34)", 3); testGeometry("POINT Z (2.7 -3 34)", 3);
} }
...@@ -76,7 +109,6 @@ public class TestGeometryUtils extends TestBase { ...@@ -76,7 +109,6 @@ public class TestGeometryUtils extends TestBase {
testGeometry("LINESTRING (-1 -2, 10 1, 2 20)", 2); testGeometry("LINESTRING (-1 -2, 10 1, 2 20)", 2);
testGeometry("LINESTRING (1 2, 1 3)", 2); testGeometry("LINESTRING (1 2, 1 3)", 2);
testGeometry("LINESTRING (1 2, 2 2)", 2); testGeometry("LINESTRING (1 2, 2 2)", 2);
testGeometry("LINESTRING (1 NaN, 2 NaN)", 2);
testGeometry("LINESTRING EMPTY", 2); testGeometry("LINESTRING EMPTY", 2);
testGeometry("LINESTRING Z (-1 -2 -3, 10 15.7 3)", 3); testGeometry("LINESTRING Z (-1 -2 -3, 10 15.7 3)", 3);
} }
...@@ -227,6 +259,27 @@ public class TestGeometryUtils extends TestBase { ...@@ -227,6 +259,27 @@ public class TestGeometryUtils extends TestBase {
testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYZM, ewkb); testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYZM, ewkb);
} }
private void testFiniteOnly() {
for (int i = 0; i < NON_FINITE.length; i++) {
testFiniteOnly(NON_FINITE[i], new EWKBTarget(new ByteArrayOutputStream(), NON_FINITE_DIMENSIONS[i]));
}
for (int i = 0; i < NON_FINITE.length; i++) {
testFiniteOnly(NON_FINITE[i], new EWKTTarget(new StringBuilder(), NON_FINITE_DIMENSIONS[i]));
}
for (int i = 0; i < NON_FINITE.length; i++) {
testFiniteOnly(NON_FINITE[i], new GeometryTarget(NON_FINITE_DIMENSIONS[i]));
}
}
private void testFiniteOnly(byte[] ewkb, Target target) {
try {
EWKBUtils.parseEWKB(ewkb, target);
fail(target.getClass().getName() + ' ' + StringUtils.convertBytesToHex(ewkb));
} catch (IllegalArgumentException e) {
// Expected
}
}
private void testSRID() throws Exception { private void testSRID() throws Exception {
byte[] ewkb = EWKTUtils.ewkt2ewkb("SRID=10;GEOMETRYCOLLECTION (POINT (1 2))"); byte[] ewkb = EWKTUtils.ewkt2ewkb("SRID=10;GEOMETRYCOLLECTION (POINT (1 2))");
assertEquals(StringUtils.convertHexToBytes("" assertEquals(StringUtils.convertHexToBytes(""
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论