< Summary

Class:Itinero.Network.Tiles.NetworkTile
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Attributes.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Diffs.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Geo.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Serialization.cs
/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.TurnCosts.cs
Covered lines:909
Uncovered lines:287
Coverable lines:1196
Total lines:1891
Line coverage:76% (909 of 1196)
Covered branches:284
Total branches:370
Branch coverage:76.7% (284 of 370)
Tag:263_26948838820

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
SetAttributes(...)83.33%1280%
GetGlobalEdgeId(...)50%4100%
GetAttributes()87.5%880%
AddOrGetString(...)100%6100%
WriteAttributesTo(...)100%4100%
ReadAttributesFrom(...)100%4100%
ReadAttributesFrom(...)0%20%
WriteAttributesTo(...)0%20%
.ctor(...)100%2100%
.ctor(...)100%1100%
Clone()100%1100%
get_TileId()100%1100%
get_VertexCount()100%1100%
get_EdgeTypeMapId()100%1100%
AddVertex(...)100%1100%
HasVertex(...)100%2100%
TryGetVertex(...)100%2100%
AddEdge(...)80.76%2691.54%
DeleteEdge(...)100%2100%
IsEdgeDeleted(...)100%2100%
get_HasDeletedEdges()100%1100%
RemoveDeletedEdges()83.33%2488.88%
CloneForEdgeTypeMap(...)66.66%1283.07%
VertexEdgePointer(...)100%1100%
EncodeVertex(...)100%6100%
DecodeVertex(...)100%2100%
DecodeEdgeCrossId(...)100%1100%
GetEdgeCrossPointer(...)100%1100%
EncodePointer(...)100%2100%
DecodePointer(...)100%1100%
SetDynamicUIn32Nullable(...)100%2100%
GetTailHeadOrder(...)100%1100%
DecodeEdgePointerId(...)100%1100%
WriteEdgesAndVerticesTo(...)83.33%682.35%
ReadEdgesAndVerticesFrom(...)83.33%685%
ReadEdgesAndVerticesFrom(...)0%40%
WriteEdgesAndVerticesTo(...)0%40%
.ctor(...)100%1100%
get_MaxLonDiff()100%1100%
get_MaxLatDiff()100%1100%
InvalidateDiffs()100%1100%
EnsureDiffs(...)80%5087.69%
.ctor(...)100%1100%
SetCoordinate(...)77.77%1876.31%
GetCoordinate(...)100%4100%
SetShape(...)93.75%1696.77%
GetShape()86.36%2291.83%
WriteGeoTo(...)100%6100%
ReadGeoFrom(...)100%6100%
ReadGeoFrom(...)0%20%
WriteGeoTo(...)0%20%
WriteTo(...)100%1100%
ReadFrom(...)50%286.66%
ReadFromBuffer(...)0%20%
ReadFrom(...)100%10%
ToBytes()0%20%
WriteToBuffer(...)100%10%
GetSerializedSizeUpperBound()0%40%
.ctor(...)100%1100%
AddTurnCosts(...)85%4081.44%
GetTurnCosts()68.18%2263.79%
SetTailHeadOrder(...)85.71%1494.28%
WriteTurnCostsTo(...)50%450%
ReadTurnCostsFrom(...)50%457.14%
ReadTurnCostsFrom(...)0%20%
WriteTurnCostsTo(...)0%20%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Attributes.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using Itinero.Data;
 5using Itinero.IO;
 6using Itinero.Network.Storage;
 7using Itinero.Network.Tiles.Standalone.Global;
 8
 9namespace Itinero.Network.Tiles;
 10
 11internal partial class NetworkTile
 12{
 13    /// <summary>
 14    /// Stores the attributes, starting with the number of attributes and then alternating key-value pairs.
 15    /// </summary>
 16    private byte[] _attributes;
 17
 62418    private uint _nextAttributePointer = 0;
 19
 20    /// <summary>
 21    /// Stores each string once.
 22    /// </summary>
 23    private string[] _strings;
 24
 62425    private uint _nextStringId = 0;
 26
 27    private uint SetAttributes(IEnumerable<(string key, string value)> attributes, GlobalEdgeId? globalEdgeId)
 5747228    {
 29        // ensure enough space for the globalEdgeId header (up to 20 bytes).
 5747230        if (_attributes.Length <= _nextAttributePointer + 20)
 164531        {
 164532            Array.Resize(ref _attributes, _attributes.Length + 256);
 164533        }
 34
 35        // save position before globalEdgeId — GetAttributes/GetGlobalEdgeId read from here.
 5747236        var start = _nextAttributePointer;
 37
 5747238        if (globalEdgeId == null)
 5740639        {
 5740640            _nextAttributePointer += _attributes.SetDynamicInt64Nullable(_nextAttributePointer, null);
 5740641        }
 42        else
 6643        {
 6644            _nextAttributePointer += _attributes.SetDynamicInt64Nullable(_nextAttributePointer, globalEdgeId.Value.EdgeI
 6645            _nextAttributePointer += _attributes.SetDynamicUInt32(_nextAttributePointer, globalEdgeId.Value.Tail);
 6646            _nextAttributePointer += _attributes.SetDynamicUInt32(_nextAttributePointer, globalEdgeId.Value.Head);
 6647        }
 48
 5747249        long cPos = _nextAttributePointer;
 5747250        long p = _nextAttributePointer + 1;
 5747251        var c = 0;
 65946052        foreach (var (key, value) in attributes)
 24352253        {
 24352254            if (_attributes.Length <= p + 16)
 131555            {
 131556                Array.Resize(ref _attributes, _attributes.Length + 256);
 131557            }
 58
 24352259            var id = this.AddOrGetString(key);
 24352260            p += _attributes.SetDynamicUInt32(p, id);
 24352261            id = this.AddOrGetString(value);
 24352262            p += _attributes.SetDynamicUInt32(p, id);
 63
 24352264            c++;
 24352265            if (c == 255)
 066            {
 067                _attributes[(int)cPos] = 255;
 068                c = 0;
 069                cPos = p;
 070                p++;
 071            }
 24352272        }
 73
 5747274        if (_attributes.Length <= cPos)
 075        {
 076            Array.Resize(ref _attributes, _attributes.Length + 256);
 077        }
 78
 5747279        _attributes[(int)cPos] = (byte)c;
 80
 5747281        _nextAttributePointer = (uint)p;
 82
 5747283        return start;
 5747284    }
 85
 86    internal GlobalEdgeId? GetGlobalEdgeId(uint? pointer)
 4187    {
 4188        if (pointer == null) return null;
 89
 4190        var p = pointer.Value;
 4191        p += _attributes.GetDynamicInt64Nullable(p, out var edgeId);
 4192        if (edgeId == null) return null;
 4193        p += _attributes.GetDynamicUInt32(p, out var tail);
 4194        p += _attributes.GetDynamicUInt32(p, out var head);
 95
 4196        return GlobalEdgeId.Create(edgeId.Value, tail, head);
 4197    }
 98
 99    internal IEnumerable<(string key, string value)> GetAttributes(uint? pointer)
 2667100    {
 3162101        if (pointer == null) yield break;
 102
 2172103        var p = pointer.Value;
 2172104        p += _attributes.GetDynamicInt64Nullable(p, out var edgeId);
 2172105        if (edgeId != null)
 0106        {
 0107            p += _attributes.GetDynamicUInt32(p, out _);
 0108            p += _attributes.GetDynamicUInt32(p, out _);
 0109        }
 110
 111        int count;
 112        do
 2172113        {
 2172114            count = _attributes[(int)p];
 2172115            p++;
 116
 28206117            for (var i = 0; i < count; i++)
 12224118            {
 12224119                p += _attributes.GetDynamicUInt32(p, out var keyId);
 12224120                p += _attributes.GetDynamicUInt32(p, out var valId);
 121
 12224122                yield return (_strings[(int)keyId], _strings[(int)valId]);
 11931123            }
 3758124        } while (count == 255);
 1879125    }
 126
 127    private uint AddOrGetString(string s)
 487044128    {
 58386492129        for (uint i = 0; i < _nextStringId; i++)
 29176694130        {
 29176694131            var existing = _strings[(int)i];
 29176694132            if (existing == s)
 470492133            {
 470492134                return i;
 135            }
 28706202136        }
 137
 16552138        if (_strings.Length <= _nextStringId)
 387139        {
 387140            Array.Resize(ref _strings, _strings.Length + 256);
 387141        }
 142
 16552143        var id = _nextStringId;
 16552144        _nextStringId++;
 145
 16552146        _strings[(int)id] = s;
 16552147        return id;
 487044148    }
 149
 150    private void WriteAttributesTo(Stream stream)
 8151    {
 8152        stream.WriteVarUInt32(_nextAttributePointer);
 44153        for (var i = 0; i < _nextAttributePointer; i++)
 14154        {
 14155            stream.WriteByte(_attributes[i]);
 14156        }
 157
 8158        stream.WriteVarUInt32(_nextStringId);
 36159        for (var i = 0; i < _nextStringId; i++)
 10160        {
 10161            stream.WriteWithSize(_strings[i]);
 10162        }
 8163    }
 164
 165    private void ReadAttributesFrom(Stream stream)
 8166    {
 8167        _nextAttributePointer = stream.ReadVarUInt32();
 8168        Array.Resize(ref _attributes, (int)_nextAttributePointer);
 44169        for (var i = 0; i < _nextAttributePointer; i++)
 14170        {
 14171            _attributes[i] = (byte)stream.ReadByte();
 14172        }
 173
 8174        _nextStringId = stream.ReadVarUInt32();
 8175        Array.Resize(ref _strings, (int)_nextStringId);
 36176        for (var i = 0; i < _nextStringId; i++)
 10177        {
 10178            _strings[i] = stream.ReadWithSizeString();
 10179        }
 8180    }
 181
 182    private void ReadAttributesFrom(byte[] data, ref int offset)
 0183    {
 0184        _nextAttributePointer = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0185        Array.Resize(ref _attributes, (int)_nextAttributePointer);
 0186        Buffer.BlockCopy(data, offset, _attributes, 0, (int)_nextAttributePointer);
 0187        offset += (int)_nextAttributePointer;
 188
 0189        _nextStringId = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0190        Array.Resize(ref _strings, (int)_nextStringId);
 0191        for (var i = 0; i < _nextStringId; i++)
 0192        {
 0193            _strings[i] = BitCoderBuffer.GetWithSizeString(data, ref offset);
 0194        }
 0195    }
 196
 197    private void WriteAttributesTo(byte[] data, ref int offset)
 0198    {
 0199        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextAttributePointer);
 0200        Buffer.BlockCopy(_attributes, 0, data, offset, (int)_nextAttributePointer);
 0201        offset += (int)_nextAttributePointer;
 202
 0203        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextStringId);
 0204        for (var i = 0; i < _nextStringId; i++)
 0205        {
 0206            BitCoderBuffer.SetWithSizeString(data, ref offset, _strings[i]);
 0207        }
 0208    }
 209}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using Itinero.IO;
 5using Itinero.Network.Storage;
 6using Itinero.Network.Tiles.Standalone.Global;
 7using Itinero.Network.TurnCosts;
 8
 9namespace Itinero.Network.Tiles;
 10
 11internal partial class NetworkTile
 12{
 13    private const int DefaultSizeIncrease = 16;
 14
 15    private readonly uint _tileId;
 16    private readonly int _zoom; // the zoom level.
 17    private readonly Guid _edgeTypeMapId; // the edge type index id.
 18
 62419    private uint _nextVertexId = 0; // the next vertex id.
 20
 21    // the pointers, per vertex, to their first edge.
 22    // TODO: investigate if it's worth storing these with less precision, one tile will never contain this much data.
 23    // TODO: investigate if we can not use one-increasing vertex ids but also use their pointers like with the edges.
 24    private uint[] _pointers;
 25    private uint _nextCrossTileId; // the next id for an edge that crosses tile boundaries.
 26    private uint[] _crossEdgePointers; // points to the cross tile boundary edges.
 27
 28    // the next edge id.
 62429    private uint _nextEdgeId = 0;
 30
 31    // the edges.
 32    private byte[] _edges;
 33
 34    /// <summary>
 35    /// Creates a new tile.
 36    /// </summary>
 37    /// <param name="zoom">The zoom level.</param>
 38    /// <param name="tileId">The tile id.</param>
 39    /// <param name="edgeTypeMapId">The edge type index id.</param>
 60840    public NetworkTile(int zoom, uint tileId, Guid? edgeTypeMapId = null)
 60841    {
 60842        _zoom = zoom;
 60843        _tileId = tileId;
 60844        _edgeTypeMapId = edgeTypeMapId ?? Guid.Empty;
 60845        _nextCrossTileId = 0;
 46
 60847        _pointers = new uint[0];
 60848        _edges = new byte[0];
 60849        _crossEdgePointers = new uint[0];
 50
 60851        _coordinates = new byte[0];
 60852        _shapes = new byte[0];
 60853        _attributes = new byte[0];
 60854        _strings = new string[0];
 60855    }
 56
 1657    private NetworkTile(int zoom, uint tileId, Guid edgeTypeMapId, uint nextCrossTileId, uint[] pointers,
 1658        byte[] edges,
 1659        uint[] crossEdgePointers, byte[] coordinates, byte[] shapes,
 1660        byte[] attributes,
 1661        string[] strings, byte[] turnCosts, uint nextVertexId, uint nextEdgeId,
 1662        uint nextAttributePointer,
 1663        uint nextShapePointer, uint nextStringId)
 1664    {
 1665        _zoom = zoom;
 1666        _tileId = tileId;
 1667        _edgeTypeMapId = edgeTypeMapId;
 1668        _nextCrossTileId = nextCrossTileId;
 1669        _pointers = pointers;
 1670        _edges = edges;
 1671        _crossEdgePointers = crossEdgePointers;
 72
 1673        _coordinates = coordinates;
 1674        _shapes = shapes;
 1675        _attributes = attributes;
 1676        _strings = strings;
 1677        _turnCosts = turnCosts;
 78
 1679        _nextVertexId = nextVertexId;
 1680        _nextEdgeId = nextEdgeId;
 1681        _nextAttributePointer = nextAttributePointer;
 1682        _nextShapePointer = nextShapePointer;
 1683        _nextStringId = nextStringId;
 1684    }
 85
 86    /// <summary>
 87    /// Clones this graph tile.
 88    /// </summary>
 89    /// <returns>The copy of this tile.</returns>
 90    public NetworkTile Clone()
 1091    {
 1092        return new(_zoom, _tileId, _edgeTypeMapId, _nextCrossTileId, (uint[])_pointers.Clone(), (byte[])_edges.Clone(),
 1093            (uint[])_crossEdgePointers.Clone(),
 1094            (byte[])_coordinates.Clone(), (byte[])_shapes.Clone(), (byte[])_attributes.Clone(), (string[])_strings.Clone
 1095            _nextVertexId,
 1096            _nextEdgeId, _nextAttributePointer, _nextShapePointer, _nextStringId);
 1097    }
 98
 99    /// <summary>
 100    /// Gets the tile id.
 101    /// </summary>
 15462826102    public uint TileId => _tileId;
 103
 104    /// <summary>
 105    /// Gets the number of vertices.
 106    /// </summary>
 3794619107    public uint VertexCount => _nextVertexId;
 108
 109    /// <summary>
 110    /// Gets the edge type map id.
 111    /// </summary>
 4221241112    public Guid EdgeTypeMapId => _edgeTypeMapId;
 113
 114    /// <summary>
 115    /// Adds a new vertex and returns its id.
 116    /// </summary>
 117    /// <param name="longitude">The longitude.</param>
 118    /// <param name="latitude">The latitude.</param>
 119    /// <param name="e">The elevation in meters.</param>
 120    /// <returns>The ID of the new vertex.</returns>
 121    public VertexId AddVertex(double longitude, double latitude, float? e = null)
 37718122    {
 123        // set coordinate.
 37718124        this.SetCoordinate(_nextVertexId, longitude, latitude, e);
 125
 126        // create id.
 37718127        var vertexId = new VertexId(_tileId, _nextVertexId);
 37718128        _nextVertexId++;
 129
 130        // make room.
 37718131        ArrayBaseExtensions.EnsureMinimumSize(ref _pointers, vertexId.LocalId, DefaultSizeIncrease);
 132
 37718133        return vertexId;
 37718134    }
 135
 136    /// <summary>
 137    /// Returns true if the vertex exists.
 138    /// </summary>
 139    /// <param name="vertex"></param>
 140    /// <returns></returns>
 141    public bool HasVertex(VertexId vertex)
 110142    {
 110143        if (vertex.LocalId >= _nextVertexId)
 22144        {
 22145            return false;
 146        }
 147
 88148        return true;
 110149    }
 150
 151    /// <summary>
 152    /// Gets the vertex with the given id.
 153    /// </summary>
 154    /// <param name="vertex">The vertex.</param>
 155    /// <param name="longitude">The longitude.</param>
 156    /// <param name="latitude">The latitude.</param>
 157    /// <param name="elevation">The elevation.</param>
 158    /// <returns>True if the vertex exists.</returns>
 159    public bool TryGetVertex(VertexId vertex, out double longitude, out double latitude, out float? elevation)
 5612335160    {
 5612335161        longitude = default;
 5612335162        latitude = default;
 5612335163        elevation = null;
 5612335164        if (vertex.LocalId >= _nextVertexId)
 5165        {
 5166            return false;
 167        }
 168
 5612330169        this.GetCoordinate(vertex.LocalId, out longitude, out latitude, out elevation);
 5612330170        return true;
 5612335171    }
 172
 173    /// <summary>
 174    /// Adds a new edge and returns its id.
 175    /// </summary>
 176    /// <param name="vertex1">The first vertex.</param>
 177    /// <param name="vertex2">The second vertex.</param>
 178    /// <param name="shape">The shape."</param>
 179    /// <param name="attributes">The attributes."</param>
 180    /// <param name="edgeId">The edge id if this edge is a part of another tile.</param>
 181    /// <param name="edgeTypeId">The edge type id, if any.</param>
 182    /// <param name="length">The length in centimeters.</param>
 183    /// <param name="globalEdgeId">The global edge id, if any.</param>
 184    /// <returns>The new edge id.</returns>
 185    public EdgeId AddEdge(VertexId vertex1, VertexId vertex2,
 186        IEnumerable<(double longitude, double latitude, float? e)>? shape = null,
 187        IEnumerable<(string key, string value)>? attributes = null, EdgeId? edgeId = null, uint? edgeTypeId = null,
 188        uint? length = null, GlobalEdgeId? globalEdgeId = null)
 53036189    {
 190        // Edge set changed → cached MaxLonDiff/MaxLatDiff are now stale.
 53036191        this.InvalidateDiffs();
 192
 53036193        if (vertex2.TileId != _tileId)
 2343194        {
 195            // this edge crosses tiles boundaries, it need special treatment and a stable id.
 196            // because the edge originates in this tile, this tile is responsible for generating the id.
 2343197            if (edgeId != null) throw new ArgumentException(
 0198                    "The edge id shouldn't be a given, it should be generated by the originating tile.",
 0199                    nameof(edgeId));
 2343200            if (vertex1.TileId != _tileId) throw new ArgumentException("None of the two vertices in this edge are in thi
 0201                    nameof(vertex1));
 202
 203            // generate a new cross tile id and store pointer to edge.
 2343204            edgeId = EdgeId.CrossEdgeId(_tileId, _nextCrossTileId);
 2343205            ArrayBaseExtensions.EnsureMinimumSize(ref _crossEdgePointers, _nextCrossTileId + 1);
 2343206            _crossEdgePointers[_nextCrossTileId] = _nextEdgeId;
 2343207            _nextCrossTileId++;
 2343208        }
 50693209        else if (vertex1.TileId != _tileId)
 2342210        {
 211            // this edge crosses tiles boundaries, it need special treatment and a stable id.
 212            // because the edge originates in another tile it should already have an id.
 2342213            if (edgeId == null) throw new ArgumentException(
 0214                    "Cannot add an edge that doesn't start in this tile without a proper edge id.",
 0215                    nameof(edgeId));
 2342216            if (edgeId.Value.TileId != vertex1.TileId) throw new ArgumentException("The edge id doesn't match the tile i
 0217                    nameof(edgeId));
 2342218        }
 219        else
 48351220        {
 221            // this edge starts in this tile, it get an id from this tile.
 48351222            edgeId = new EdgeId(_tileId, _nextEdgeId);
 48351223        }
 224
 225        // write the edge data.
 53036226        var newEdgePointer = _nextEdgeId;
 53036227        var size = EncodeVertex(ref _edges, _tileId, _nextEdgeId, vertex1);
 53036228        _nextEdgeId += size;
 53036229        size = EncodeVertex(ref _edges, _tileId, _nextEdgeId, vertex2);
 53036230        _nextEdgeId += size;
 231
 232        // get previous pointers if vertex already has edges
 233        // set the new pointers.
 53036234        uint? v1p = null;
 53036235        if (vertex1.TileId == _tileId)
 50694236        {
 50694237            v1p = _pointers[vertex1.LocalId].DecodeNullableData();
 50694238            _pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 50694239        }
 240
 53036241        uint? v2p = null;
 53036242        if (vertex2.TileId == _tileId)
 50693243        {
 50693244            v2p = _pointers[vertex2.LocalId].DecodeNullableData();
 50693245            _pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 50693246        }
 247
 248        // set next pointers.
 53036249        size = EncodePointer(ref _edges, _nextEdgeId, v1p);
 53036250        _nextEdgeId += size;
 53036251        size = EncodePointer(ref _edges, _nextEdgeId, v2p);
 53036252        _nextEdgeId += size;
 253
 254        // write edge id explicitly if not in this edge.
 53036255        if (vertex1.TileId != vertex2.TileId)
 4685256        {
 257            // this data will only be there for edges crossing tile boundaries.
 4685258            _nextEdgeId += (uint)_edges.SetDynamicUInt32(_nextEdgeId,
 4685259                edgeId.Value.LocalId - EdgeId.MinCrossId);
 4685260        }
 261
 262        // write edge profile id.
 53036263        _nextEdgeId += SetDynamicUIn32Nullable(ref _edges, _nextEdgeId, edgeTypeId);
 264
 265        // write length.
 53036266        _nextEdgeId += SetDynamicUIn32Nullable(ref _edges, _nextEdgeId, length);
 267
 268        // set tail and head order.
 53036269        _edges.SetTailHeadOrder(_nextEdgeId, null, null);
 53036270        _nextEdgeId++;
 271
 272        // take care of shape if any.
 53036273        uint? shapePointer = null;
 53036274        if (shape != null)
 52565275        {
 52565276            shapePointer = this.SetShape(shape);
 52565277        }
 278
 53036279        size = EncodePointer(ref _edges, _nextEdgeId, shapePointer);
 53036280        _nextEdgeId += size;
 281
 282        // take care of attributes if any.
 53036283        uint? attributesPointer = null;
 53036284        if (attributes != null || globalEdgeId != null)
 52673285        {
 52673286            attributesPointer = this.SetAttributes(attributes ?? [], globalEdgeId);
 52673287        }
 288
 53036289        size = EncodePointer(ref _edges, _nextEdgeId, attributesPointer);
 53036290        _nextEdgeId += size;
 291
 53036292        return edgeId.Value;
 53036293    }
 294
 295    private HashSet<EdgeId>? _deletedEdges;
 296
 297    /// <summary>
 298    /// Marks the given edge as deleted.
 299    /// </summary>
 300    /// <param name="edge">The edge to delete.</param>
 301    internal void DeleteEdge(EdgeId edge)
 10302    {
 10303        _deletedEdges ??= [];
 10304        _deletedEdges.Add(edge);
 10305        this.InvalidateDiffs();
 10306    }
 307
 308    /// <summary>
 309    /// Returns true if the edge is deleted.
 310    /// </summary>
 311    /// <param name="edge">The edge.</param>
 312    /// <returns>True if the edge was deleted.</returns>
 313    internal bool IsEdgeDeleted(EdgeId edge)
 6466583314    {
 12933160315        if (_deletedEdges == null) return false;
 316
 6317        return _deletedEdges.Contains(edge);
 6466583318    }
 319
 320    /// <summary>
 321    /// Returns true if there are deleted edges.
 322    /// </summary>
 500323    internal bool HasDeletedEdges => _deletedEdges != null;
 324
 325    /// <summary>
 326    /// Removes all the deleted edges.
 327    /// </summary>
 328    /// <remarks>This changes all the edges if all edges written after a deleted edge.</remarks>
 329    internal void RemoveDeletedEdges()
 10330    {
 10331        if (_deletedEdges == null) return;
 10332        this.InvalidateDiffs();
 333
 334        // reset vertex pointers.
 340335        for (var i = 0; i < _pointers.Length; i++)
 160336        {
 160337            _pointers[i] = 0;
 160338        }
 339
 340        // redo edges, skipping deleted edges.
 10341        var nextEdgeId = _nextEdgeId;
 10342        var p = 0U;
 10343        var newP = 0U;
 24344        while (p < nextEdgeId)
 14345        {
 346            // read edge data.
 14347            var currentEdgeId = p;
 14348            p += this.DecodeVertex(p, out var local1Id, out var tile1Id);
 14349            var vertex1 = new VertexId(tile1Id, local1Id);
 14350            p += this.DecodeVertex(p, out var local2Id, out var tile2Id);
 14351            var vertex2 = new VertexId(tile2Id, local2Id);
 14352            p += this.DecodePointer(p, out _);
 14353            p += this.DecodePointer(p, out _);
 14354            uint? crossEdgeId = null;
 14355            if (tile1Id != tile2Id)
 4356            {
 4357                p += _edges.GetDynamicUInt32(p, out var c);
 4358                crossEdgeId = c;
 4359            }
 360
 14361            p += _edges.GetDynamicUInt32Nullable(p, out var edgeTypeId);
 14362            p += _edges.GetDynamicUInt32Nullable(p, out var length);
 14363            var tailHeadOrder = _edges[p];
 14364            p++;
 14365            p += this.DecodePointer(p, out var shapePointer);
 14366            p += this.DecodePointer(p, out var attributePointer);
 367
 368            // check if edge was deleted.
 20369            if (_deletedEdges.Contains(new EdgeId(_tileId, currentEdgeId))) continue;
 8370            if (crossEdgeId.HasValue)
 4371            {
 8372                if (_deletedEdges.Contains(EdgeId.CrossEdgeId(vertex1.TileId, crossEdgeId.Value))) continue;
 0373            }
 374
 375            // no need to overwrite identical data.
 4376            if (p == newP) continue;
 377
 378            // write edge data again.
 4379            var newEdgePointer = newP;
 4380            newP += EncodeVertex(ref _edges, _tileId, newP, vertex1);
 4381            newP += EncodeVertex(ref _edges, _tileId, newP, vertex2);
 4382            uint? v1p = null;
 4383            if (vertex1.TileId == _tileId)
 4384            {
 4385                v1p = _pointers[vertex1.LocalId].DecodeNullableData();
 4386                _pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 4387            }
 388
 4389            uint? v2P = null;
 4390            if (vertex2.TileId == _tileId)
 4391            {
 4392                v2P = _pointers[vertex2.LocalId].DecodeNullableData();
 4393                _pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 4394            }
 395
 4396            newP += EncodePointer(ref _edges, newP, v1p);
 4397            newP += EncodePointer(ref _edges, newP, v2P);
 4398            if (crossEdgeId != null)
 0399            {
 0400                newP += (uint)_edges.SetDynamicUInt32(newP, crossEdgeId.Value);
 0401                if (vertex1.TileId == _tileId)
 0402                {
 0403                    _crossEdgePointers[crossEdgeId.Value] = newEdgePointer;
 0404                }
 0405            }
 406
 4407            newP += _edges.SetDynamicUInt32Nullable(newP, edgeTypeId);
 4408            newP += _edges.SetDynamicUInt32Nullable(newP, length);
 4409            _edges[newP] = tailHeadOrder;
 4410            newP++;
 4411            newP += EncodePointer(ref _edges, newP, shapePointer);
 4412            newP += EncodePointer(ref _edges, newP, attributePointer);
 4413        }
 414
 10415        _nextEdgeId = newP;
 10416        _deletedEdges = null;
 10417    }
 418
 419    internal NetworkTile CloneForEdgeTypeMap(
 420        (Guid id, Func<IEnumerable<(string key, string value)>, uint> func) edgeTypeMap)
 6421    {
 6422        var edges = new byte[_edges.Length];
 6423        var pointers = new uint[_pointers.Length];
 6424        var crossEdgePointers = new uint[_crossEdgePointers.Length];
 6425        var nextEdgeId = _nextEdgeId;
 6426        var p = 0U;
 6427        var newP = 0U;
 16428        while (p < nextEdgeId)
 10429        {
 430            // read edge data.
 10431            p += this.DecodeVertex(p, out var local1Id, out var tile1Id);
 10432            var vertex1 = new VertexId(tile1Id, local1Id);
 10433            p += this.DecodeVertex(p, out var local2Id, out var tile2Id);
 10434            var vertex2 = new VertexId(tile2Id, local2Id);
 10435            p += this.DecodePointer(p, out _);
 10436            p += this.DecodePointer(p, out _);
 10437            uint? crossEdgeId = null;
 10438            if (tile1Id != tile2Id)
 0439            {
 0440                p += (uint)_edges.GetDynamicUInt32(p, out var c);
 0441                crossEdgeId = c;
 0442            }
 443
 10444            p += _edges.GetDynamicUInt32Nullable(p, out var _);
 10445            p += _edges.GetDynamicUInt32Nullable(p, out var length);
 10446            var tailHeadOrder = _edges[p];
 10447            p++;
 10448            p += this.DecodePointer(p, out var shapePointer);
 10449            p += this.DecodePointer(p, out var attributePointer);
 450
 451            // generate new edge type id.
 10452            var newEdgeTypeId = edgeTypeMap.func(this.GetAttributes(attributePointer));
 453
 454            // write edge data again.
 10455            var newEdgePointer = newP;
 10456            newP += EncodeVertex(ref edges, _tileId, newP, vertex1);
 10457            newP += EncodeVertex(ref edges, _tileId, newP, vertex2);
 10458            uint? v1p = null;
 10459            if (vertex1.TileId == _tileId)
 10460            {
 10461                v1p = pointers[vertex1.LocalId].DecodeNullableData();
 10462                pointers[vertex1.LocalId] = newEdgePointer.EncodeToNullableData();
 10463            }
 464
 10465            uint? v2p = null;
 10466            if (vertex2.TileId == _tileId)
 10467            {
 10468                v2p = pointers[vertex2.LocalId].DecodeNullableData();
 10469                pointers[vertex2.LocalId] = newEdgePointer.EncodeToNullableData();
 10470            }
 471
 10472            newP += EncodePointer(ref edges, newP, v1p);
 10473            newP += EncodePointer(ref edges, newP, v2p);
 10474            if (crossEdgeId != null)
 0475            {
 0476                newP += edges.SetDynamicUInt32(newP, crossEdgeId.Value);
 0477                if (vertex1.TileId == _tileId)
 0478                {
 0479                    crossEdgePointers[crossEdgeId.Value] = newEdgePointer;
 0480                }
 0481            }
 482
 10483            newP += edges.SetDynamicUInt32Nullable(newP, newEdgeTypeId);
 10484            newP += edges.SetDynamicUInt32Nullable(newP, length);
 10485            edges[newP] = tailHeadOrder;
 10486            newP++;
 10487            newP += EncodePointer(ref edges, newP, shapePointer);
 10488            newP += EncodePointer(ref edges, newP, attributePointer);
 10489        }
 490
 6491        return new NetworkTile(_zoom, _tileId, edgeTypeMap.id, _nextCrossTileId, pointers, edges, crossEdgePointers,
 6492            _coordinates,
 6493            _shapes, _attributes, _strings, _turnCosts, _nextVertexId, _nextEdgeId,
 6494            _nextAttributePointer, _nextShapePointer, _nextStringId);
 6495    }
 496
 497    internal uint VertexEdgePointer(uint vertex)
 1100192498    {
 1100192499        return _pointers[vertex];
 1100192500    }
 501
 502    internal static byte EncodeVertex(ref byte[] edges, uint localTileId, uint location, VertexId vertexId)
 106100503    {
 106100504        if (vertexId.TileId == localTileId)
 101415505        {
 506            // same tile, only store local id.
 101415507            if (edges.Length <= location + 5)
 12965508            {
 12965509                Array.Resize(ref edges, edges.Length + DefaultSizeIncrease);
 12965510            }
 511
 101415512            return edges.SetDynamicUInt32(location, vertexId.LocalId);
 513        }
 514
 515        // other tile, store full id.
 4685516        if (edges.Length <= location + 10)
 2013517        {
 2013518            Array.Resize(ref edges, edges.Length + DefaultSizeIncrease);
 2013519        }
 520
 4685521        var encodedId = vertexId.Encode();
 4685522        return edges.SetDynamicUInt64(location, encodedId);
 106100523    }
 524
 525    internal byte DecodeVertex(uint location, out uint localId, out uint tileId)
 12942474526    {
 12942474527        var size = _edges.GetDynamicUInt64(location, out var encodedId);
 12942474528        if (encodedId < uint.MaxValue)
 12829363529        {
 12829363530            localId = (uint)encodedId;
 12829363531            tileId = _tileId;
 12829363532            return size;
 533        }
 534
 113111535        VertexId.Decode(encodedId, out tileId, out localId);
 113111536        return size;
 12942474537    }
 538
 539    internal byte DecodeEdgeCrossId(uint location, out uint edgeCrossId)
 113005540    {
 113005541        var s = _edges.GetDynamicUInt32(location, out var c);
 113005542        edgeCrossId = EdgeId.MinCrossId + c;
 113005543        return s;
 113005544    }
 545
 546    internal uint GetEdgeCrossPointer(uint edgeCrossId)
 51809547    {
 51809548        return _crossEdgePointers[edgeCrossId];
 51809549    }
 550
 551    internal static byte EncodePointer(ref byte[] edges, uint location, uint? pointer)
 212200552    {
 553        // TODO: save the diff instead of the full pointer.
 212200554        if (edges.Length <= location + 5)
 27894555        {
 27894556            Array.Resize(ref edges, edges.Length + DefaultSizeIncrease);
 27894557        }
 558
 212200559        return edges.SetDynamicUInt32(location,
 212200560            pointer.EncodeAsNullableData());
 212200561    }
 562
 563    internal byte DecodePointer(uint location, out uint? pointer)
 14232614564    {
 14232614565        var size = _edges.GetDynamicUInt32(location, out var data);
 14232614566        pointer = data.DecodeNullableData();
 14232614567        return size;
 14232614568    }
 569
 570    internal static byte SetDynamicUIn32Nullable(ref byte[] edges, uint pointer, uint? data)
 106072571    {
 117003572        while (edges.Length <= pointer + 5)
 10931573        {
 10931574            Array.Resize(ref edges, edges.Length + DefaultSizeIncrease);
 10931575        }
 576
 106072577        return edges.SetDynamicUInt32Nullable(pointer, data);
 106072578    }
 579
 580    internal void GetTailHeadOrder(uint location, ref byte? tail, ref byte? head)
 6471212581    {
 6471212582        _edges.GetTailHeadOrder(location, ref tail, ref head);
 6471212583    }
 584
 585    internal byte DecodeEdgePointerId(uint location, out uint? edgeProfileId)
 12942424586    {
 12942424587        return _edges.GetDynamicUInt32Nullable(location, out edgeProfileId);
 12942424588    }
 589
 590    private void WriteEdgesAndVerticesTo(Stream stream)
 8591    {
 592        // write vertex pointers.
 8593        stream.WriteVarUInt32(_nextVertexId);
 42594        for (var i = 0; i < _nextVertexId; i++)
 13595        {
 13596            stream.WriteVarUInt32(_pointers[i]);
 13597        }
 598
 599        // write edges.
 8600        stream.WriteVarUInt32(_nextEdgeId);
 106601        for (var i = 0; i < _nextEdgeId; i++)
 45602        {
 45603            stream.WriteByte(_edges[i]);
 45604        }
 605
 606        // write cross edge pointers.
 8607        stream.WriteVarUInt32(_nextCrossTileId);
 16608        for (var i = 0; i < _nextCrossTileId; i++)
 0609        {
 0610            stream.WriteVarUInt32(_crossEdgePointers[i]);
 0611        }
 8612    }
 613
 614    private void ReadEdgesAndVerticesFrom(Stream stream)
 8615    {
 616        // read vertex pointers.
 8617        _nextVertexId = stream.ReadVarUInt32();
 8618        Array.Resize(ref _pointers, (int)_nextVertexId);
 42619        for (var i = 0; i < _nextVertexId; i++)
 13620        {
 13621            _pointers[i] = stream.ReadVarUInt32();
 13622        }
 623
 624        // read edges.
 8625        _nextEdgeId = stream.ReadVarUInt32();
 8626        Array.Resize(ref _edges, (int)_nextEdgeId);
 106627        for (var i = 0; i < _nextEdgeId; i++)
 45628        {
 45629            _edges[i] = (byte)stream.ReadByte();
 45630        }
 631
 632        // read cross tile edge pointers.
 8633        _nextCrossTileId = stream.ReadVarUInt32();
 8634        Array.Resize(ref _crossEdgePointers, (int)_nextCrossTileId);
 16635        for (var i = 0; i < _nextCrossTileId; i++)
 0636        {
 0637            _crossEdgePointers[i] = stream.ReadVarUInt32();
 0638        }
 8639    }
 640
 641    private void ReadEdgesAndVerticesFrom(byte[] data, ref int offset)
 0642    {
 0643        _nextVertexId = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0644        Array.Resize(ref _pointers, (int)_nextVertexId);
 0645        for (var i = 0; i < _nextVertexId; i++)
 0646        {
 0647            _pointers[i] = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0648        }
 649
 0650        _nextEdgeId = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0651        Array.Resize(ref _edges, (int)_nextEdgeId);
 0652        Buffer.BlockCopy(data, offset, _edges, 0, (int)_nextEdgeId);
 0653        offset += (int)_nextEdgeId;
 654
 0655        _nextCrossTileId = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0656        Array.Resize(ref _crossEdgePointers, (int)_nextCrossTileId);
 0657        for (var i = 0; i < _nextCrossTileId; i++)
 0658        {
 0659            _crossEdgePointers[i] = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0660        }
 0661    }
 662
 663    private void WriteEdgesAndVerticesTo(byte[] data, ref int offset)
 0664    {
 0665        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextVertexId);
 0666        for (var i = 0; i < _nextVertexId; i++)
 0667        {
 0668            BitCoderBuffer.SetVarUInt32(data, ref offset, _pointers[i]);
 0669        }
 670
 0671        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextEdgeId);
 0672        Buffer.BlockCopy(_edges, 0, data, offset, (int)_nextEdgeId);
 0673        offset += (int)_nextEdgeId;
 674
 0675        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextCrossTileId);
 0676        for (var i = 0; i < _nextCrossTileId; i++)
 0677        {
 0678            BitCoderBuffer.SetVarUInt32(data, ref offset, _crossEdgePointers[i]);
 0679        }
 0680    }
 681}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Diffs.cs

#LineLine coverage
 1namespace Itinero.Network.Tiles;
 2
 3/// <summary>
 4/// Per-tile geographic extent bounds used by the tile-bounded snap algorithm
 5/// in <c>EdgeSearch.SnapInBoxAsync</c>. See
 6/// <c>src/Itinero/Network/Search/snap-algorithm.md</c> for the design.
 7///
 8/// <para>
 9/// <see cref="MaxLonDiff"/> and <see cref="MaxLatDiff"/> bound how far any
 10/// edge starting at a vertex in this tile can reach in each axis. They are
 11/// the maxima of (edge.bbox.maxLon − edge.bbox.minLon) and
 12/// (edge.bbox.maxLat − edge.bbox.minLat) over every edge with at least one
 13/// vertex in the tile. Cross-tile edges contribute to both endpoint tiles'
 14/// diffs (each tile sees them once during its own per-vertex iteration).
 15/// </para>
 16///
 17/// <para>
 18/// Computed lazily on first access via <see cref="EnsureDiffs"/>, which
 19/// takes a network reference so that cross-tile edges' remote endpoints
 20/// can be resolved to their actual coordinates. The lazy contract: the
 21/// snap path's <c>NotifyBox</c> has already loaded the partner tiles by
 22/// the time the snap algorithm asks for diffs. If a partner tile somehow
 23/// isn't loaded, we fall back to the partner tile's geographic bbox
 24/// (~tile-width-wide over-estimate) — correct but loose.
 25/// </para>
 26///
 27/// <para>
 28/// Invalidated to <see cref="double.NaN"/> whenever an edge is added or
 29/// removed, so late boundary edges added by neighbouring tiles'
 30/// <c>AddStandaloneTile</c> calls don't leave stale values.
 31/// </para>
 32/// </summary>
 33internal partial class NetworkTile
 34{
 62435    private double _maxLonDiff = double.NaN;
 62436    private double _maxLatDiff = double.NaN;
 37
 38    /// <summary>
 39    /// Max lon-span across all edges with a vertex in this tile. Returns
 40    /// <see cref="double.NaN"/> if <see cref="EnsureDiffs"/> hasn't been
 41    /// called yet. Callers should invoke <see cref="EnsureDiffs"/> first.
 42    /// </summary>
 1062543    public double MaxLonDiff => _maxLonDiff;
 44
 45    /// <summary>
 46    /// Max lat-span across all edges with a vertex in this tile. See
 47    /// <see cref="MaxLonDiff"/>.
 48    /// </summary>
 1062549    public double MaxLatDiff => _maxLatDiff;
 50
 51    /// <summary>
 52    /// Reset the cached diffs. Call from any mutation that could change
 53    /// the tile's edge set (AddEdge, DeleteEdge, RemoveDeletedEdges).
 54    /// </summary>
 55    private void InvalidateDiffs()
 5305656    {
 5305657        _maxLonDiff = double.NaN;
 5305658        _maxLatDiff = double.NaN;
 5305659    }
 60
 61    /// <summary>
 62    /// Compute the diffs if not already cached. Takes a network reference
 63    /// so cross-tile edges can resolve their remote endpoint's actual
 64    /// coordinate. If the partner tile isn't loaded, falls back to the
 65    /// partner's geographic bbox (correct but loose).
 66    /// </summary>
 67    internal void EnsureDiffs(Itinero.Network.Enumerators.Edges.IEdgeEnumerable network)
 539368    {
 1064069        if (!double.IsNaN(_maxLonDiff)) return;
 70
 71        // Walk every edge once per endpoint that's in this tile. Intra-tile
 72        // edges are seen twice; max is idempotent so that's fine. Cross-tile
 73        // edges are seen once (from the local endpoint's pointer chain),
 74        // which is enough — the partner tile sees them independently from
 75        // its own side.
 14676        double maxLon = 0;
 14677        double maxLat = 0;
 78
 14679        var enumerator = new NetworkTileEnumerator();
 14680        enumerator.MoveTo(this);
 81
 5253482        for (uint v = 0; v < _nextVertexId; v++)
 2612183        {
 2612184            var vertexId = new VertexId(_tileId, v);
 2612185            if (!enumerator.MoveTo(vertexId)) continue;
 2612186            if (!this.TryGetVertex(vertexId, out var tailLon, out var tailLat, out _)) continue;
 87
 9664188            while (enumerator.MoveNext())
 7052089            {
 7052090                var minLon = tailLon;
 7052091                var maxLonE = tailLon;
 7052092                var minLat = tailLat;
 7052093                var maxLatE = tailLat;
 94
 95                // Resolve the head endpoint's actual coordinate. For
 96                // intra-tile edges it's right here; for cross-tile edges
 97                // we ask the network for the partner tile.
 7052098                var headResolved = false;
 14104099                double headLon = 0, headLat = 0;
 70520100                if (enumerator.Head.TileId == _tileId)
 68138101                {
 68138102                    if (this.TryGetVertex(enumerator.Head, out headLon, out headLat, out _))
 68138103                    {
 68138104                        headResolved = true;
 68138105                    }
 68138106                }
 107                else
 2382108                {
 2382109                    var partnerTile = network.GetTileForRead(enumerator.Head.TileId);
 2382110                    if (partnerTile != null &&
 2382111                        partnerTile.TryGetVertex(enumerator.Head, out headLon, out headLat, out _))
 2382112                    {
 2382113                        headResolved = true;
 2382114                    }
 2382115                }
 116
 70520117                if (headResolved)
 70520118                {
 104478119                    if (headLon < minLon) minLon = headLon;
 70377120                    else if (headLon > maxLonE) maxLonE = headLon;
 104342121                    if (headLat < minLat) minLat = headLat;
 70554122                    else if (headLat > maxLatE) maxLatE = headLat;
 70520123                }
 124                else
 0125                {
 126                    // Partner tile isn't loaded — fall back to its bbox.
 127                    // Over-estimates by up to ~one tile-width but stays
 128                    // correct (the per-vertex predicate built on this
 129                    // bound will be looser, never wrongly prune).
 0130                    var (hMinLon, hMinLat, hMaxLon, hMaxLat) =
 0131                        TileStatic.GetTileBoundingBox(_zoom, enumerator.Head.TileId);
 0132                    if (hMinLon < minLon) minLon = hMinLon;
 0133                    if (hMaxLon > maxLonE) maxLonE = hMaxLon;
 0134                    if (hMinLat < minLat) minLat = hMinLat;
 0135                    if (hMaxLat > maxLatE) maxLatE = hMaxLat;
 0136                }
 137
 138                // Shape points (intermediate).
 381010139                foreach (var (sLon, sLat, _) in enumerator.Shape)
 84725140                {
 89065141                    if (sLon < minLon) minLon = sLon;
 85039142                    else if (sLon > maxLonE) maxLonE = sLon;
 89925143                    if (sLat < minLat) minLat = sLat;
 83626144                    else if (sLat > maxLatE) maxLatE = sLat;
 84725145                }
 146
 70520147                var dLon = maxLonE - minLon;
 70520148                var dLat = maxLatE - minLat;
 70981149                if (dLon > maxLon) maxLon = dLon;
 70957150                if (dLat > maxLat) maxLat = dLat;
 70520151            }
 26121152        }
 153
 146154        _maxLonDiff = maxLon;
 146155        _maxLatDiff = maxLat;
 5393156    }
 157}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Geo.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using Itinero.IO;
 5using Itinero.Network.Storage;
 6
 7namespace Itinero.Network.Tiles;
 8
 9internal partial class NetworkTile
 10{
 11    private const int CoordinateSizeInBytes = 3; // 3 bytes = 24 bits = 4096 x 4096.
 12    private const int TileResolutionInBits = CoordinateSizeInBytes * 8 / 2;
 13    private const int ElevationSizeInBytes = 2; // 2 bytes = 16 bits = [-32768, 32767], using dm as resolution
 14
 15    // the vertex coordinates.
 16    private byte[] _coordinates;
 17    private int? _elevation; // the tile elevation.
 18
 19    // the shapes.
 62420    private uint _nextShapePointer = 0;
 21    private byte[] _shapes;
 22
 23    private void SetCoordinate(uint localId, double longitude, double latitude, float? e)
 3771824    {
 25        // set elevation if needed.
 3771826        if (_elevation != null && e == null)
 027        {
 028            throw new ArgumentNullException(nameof(e),
 029                "Elevation was set before, either always set elevation or never set elevation.");
 30        }
 31
 3771832        if (_elevation == null && e != null)
 1333        {
 1334            if (localId != 0)
 035            {
 036                throw new ArgumentNullException(nameof(e),
 037                    "Elevation was not set before, either always set elevation or never set elevation.");
 38            }
 39
 1340            _elevation = (int)(e * 10);
 1341        }
 42
 43        // make sure coordinates fit.
 44        uint tileCoordinatePointer;
 3771845        if (_elevation == null)
 3768046        {
 47            // don't store elevation.
 3768048            tileCoordinatePointer = localId * CoordinateSizeInBytes * 2;
 3768049            ArrayBaseExtensions.EnsureMinimumSize(ref _coordinates, tileCoordinatePointer + (CoordinateSizeInBytes * 2),
 3768050                DefaultSizeIncrease);
 3768051        }
 52        else
 3853        {
 54            // store elevation.
 3855            tileCoordinatePointer = localId * ((CoordinateSizeInBytes * 2) + ElevationSizeInBytes);
 3856            ArrayBaseExtensions.EnsureMinimumSize(ref _coordinates, tileCoordinatePointer + (CoordinateSizeInBytes * 2) 
 3857                DefaultSizeIncrease);
 3858        }
 59
 60        // write coordinates.
 61        const int resolution = (1 << TileResolutionInBits) - 1;
 3771862        var (x, y) = TileStatic.ToLocalTileCoordinates(_zoom, _tileId, longitude, latitude, resolution);
 3771863        _coordinates.SetFixed(tileCoordinatePointer, CoordinateSizeInBytes, x);
 3771864        _coordinates.SetFixed(tileCoordinatePointer + CoordinateSizeInBytes, CoordinateSizeInBytes, y);
 65
 66        // write elevation.
 3771867        if (_elevation != null)
 3868        {
 3869            if (e == null)
 070            {
 071                throw new ArgumentNullException(nameof(e),
 072                    "Elevation was set before, either always set elevation or never set elevation.");
 73            }
 74
 3875            var offset = (int)(e.Value * 10) - _elevation.Value;
 3876            _coordinates.SetFixed(tileCoordinatePointer + CoordinateSizeInBytes + CoordinateSizeInBytes,
 3877                ElevationSizeInBytes, offset);
 3878        }
 3771879    }
 80
 81    private void GetCoordinate(uint localId, out double longitude, out double latitude, out float? elevation)
 561233082    {
 561233083        var tileCoordinatePointer = _elevation == null
 561233084            ? localId * CoordinateSizeInBytes * 2
 561233085            : localId * ((CoordinateSizeInBytes * 2) + ElevationSizeInBytes);
 86
 87        const int resolution = (1 << TileResolutionInBits) - 1;
 561233088        _coordinates.GetFixed(tileCoordinatePointer, CoordinateSizeInBytes, out var x);
 561233089        _coordinates.GetFixed(tileCoordinatePointer + CoordinateSizeInBytes, CoordinateSizeInBytes, out var y);
 561233090        elevation = null;
 561233091        if (_elevation != null)
 8992        {
 8993            _coordinates.GetFixed(tileCoordinatePointer + CoordinateSizeInBytes + CoordinateSizeInBytes,
 8994                ElevationSizeInBytes, out var offset);
 8995            elevation = (_elevation.Value + offset) / 10.0f;
 8996        }
 97
 561233098        TileStatic.FromLocalTileCoordinates(_zoom, _tileId, x, y, resolution, out longitude, out latitude);
 561233099    }
 100
 101    private uint SetShape(IEnumerable<(double longitude, double latitude, float? e)> shape)
 52565102    {
 103        const int resolution = (1 << TileResolutionInBits) - 1;
 52565104        var originalPointer = _nextShapePointer;
 52565105        var blockPointer = originalPointer;
 52565106        var pointer = blockPointer + 1;
 107
 108        // make sure there is space for the block pointer.
 52565109        ArrayBaseExtensions.EnsureMinimumSize(ref _shapes, blockPointer);
 110
 52565111        var coordinateBlockSize = 8;
 52565112        if (_elevation != null)
 24113        {
 24114            coordinateBlockSize += 4;
 24115        }
 116
 52565117        using var enumerator = shape.GetEnumerator();
 52565118        var count = 0;
 52565119        (int x, int y, int? eOffset) previous = (int.MaxValue, int.MaxValue, null);
 127270120        while (enumerator.MoveNext())
 74705121        {
 74705122            var current = enumerator.Current;
 74705123            var (x, y) =
 74705124                TileStatic.ToLocalTileCoordinates(_zoom, _tileId, current.longitude, current.latitude, resolution);
 74705125            int? eOffset = null;
 74705126            var e = current.e ?? 0;
 74705127            if (_elevation != null)
 15128            {
 15129                eOffset = (int)(e * 10) - _elevation.Value;
 15130            }
 131
 132            // make sure there is space for this coordinate.
 74705133            ArrayBaseExtensions.EnsureMinimumSize(ref _shapes, pointer + coordinateBlockSize);
 134
 135            // store coordinate.
 74705136            if (count == 0)
 21047137            {
 138                // first coordinate.
 21047139                pointer += (uint)_shapes.SetDynamicInt32(pointer, x);
 21047140                pointer += (uint)_shapes.SetDynamicInt32(pointer, y);
 21047141                if (eOffset != null)
 7142                {
 7143                    pointer += (uint)_shapes.SetDynamicInt32(pointer, eOffset.Value);
 7144                }
 21047145            }
 146            else
 53658147            {
 148                // calculate diff and then store.
 53658149                var diffX = x - previous.x;
 53658150                var diffY = y - previous.y;
 53658151                pointer += (uint)_shapes.SetDynamicInt32(pointer, diffX);
 53658152                pointer += (uint)_shapes.SetDynamicInt32(pointer, diffY);
 53658153                if (eOffset != null)
 8154                {
 8155                    if (previous.eOffset == null)
 0156                    {
 0157                        throw new ArgumentException("Not all points have elevation set.");
 158                    }
 159
 8160                    var diffE = eOffset.Value - previous.eOffset.Value;
 8161                    pointer += (uint)_shapes.SetDynamicInt32(pointer, diffE);
 8162                }
 53658163            }
 164
 74705165            count++;
 166
 74705167            if (count == 255)
 1168            {
 169                // start a new block, assign 255.
 1170                _shapes[(int)blockPointer] = 255;
 1171                blockPointer = pointer;
 1172                pointer = blockPointer + 1;
 1173                count = 0;
 1174            }
 175
 74705176            previous = (x, y, eOffset);
 74705177        }
 178
 179        // a block is still open, close it.
 52565180        _shapes[(int)blockPointer] = (byte)count;
 52565181        _nextShapePointer = pointer;
 182
 52565183        return originalPointer;
 52565184    }
 185
 186    internal IEnumerable<(double longitude, double latitude, float? e)> GetShape(uint? pointer)
 1316705187    {
 1316705188        if (pointer == null)
 293189        {
 293190            yield break;
 191        }
 192
 1316412193        var p = pointer.Value;
 194
 195        const int resolution = (1 << TileResolutionInBits) - 1;
 1316412196        var count = -1;
 1316412197        (int x, int y, int? eOffset) previous = (int.MaxValue, int.MaxValue, null);
 1316413198        while (true)
 1316413199        {
 1316413200            count = _shapes[(int)p];
 1316413201            p++;
 202
 5549776203            for (var i = 0; i < count; i++)
 1459503204            {
 1459503205                p += (uint)_shapes.GetDynamicInt32(p, out var x);
 1459503206                p += (uint)_shapes.GetDynamicInt32(p, out var y);
 1459503207                int? eOffset = null;
 1459503208                if (_elevation != null)
 16209                {
 16210                    p += (uint)_shapes.GetDynamicInt32(p, out var e);
 16211                    eOffset = e;
 16212                }
 213
 1459503214                if (i > 0)
 1019811215                {
 1019811216                    x = previous.x + x;
 1019811217                    y = previous.y + y;
 1019811218                    if (_elevation != null)
 8219                    {
 8220                        if (previous.eOffset == null)
 0221                        {
 0222                            throw new ArgumentException("Not all points have elevation set.");
 223                        }
 224
 8225                        eOffset = previous.eOffset.Value + eOffset;
 8226                    }
 1019811227                }
 228
 1459503229                int? elevation = null;
 1459503230                if (_elevation != null)
 16231                {
 16232                    if (eOffset == null)
 0233                    {
 0234                        throw new ArgumentException("Not all points have elevation set.");
 235                    }
 236
 16237                    elevation = _elevation.Value + eOffset.Value;
 16238                }
 239
 1459503240                TileStatic.FromLocalTileCoordinates(_zoom, _tileId, x, y, resolution, out var longitude,
 1459503241                    out var latitude);
 1459503242                yield return (longitude, latitude, elevation / 10.0f);
 243
 1458475244                previous = (x, y, eOffset);
 1458475245            }
 246
 2630769247            if (count < 255) break;
 1248        }
 1315384249    }
 250
 251    private void WriteGeoTo(Stream stream)
 8252    {
 8253        stream.WriteVarInt32Nullable(_elevation);
 254
 255        // write vertex locations.
 8256        var coordinateSize = CoordinateSizeInBytes * 2;
 8257        if (_elevation != null)
 2258        {
 2259            coordinateSize += ElevationSizeInBytes;
 2260        }
 261
 8262        var coordinateBytes = _nextVertexId * coordinateSize;
 184263        for (var i = 0; i < coordinateBytes; i++)
 84264        {
 84265            stream.WriteByte(_coordinates[i]);
 84266        }
 267
 268        // write shape locations.
 8269        stream.WriteVarUInt32(_nextShapePointer);
 74270        for (var i = 0; i < _nextShapePointer; i++)
 29271        {
 29272            stream.WriteByte(_shapes[i]);
 29273        }
 8274    }
 275
 276    private void ReadGeoFrom(Stream stream)
 8277    {
 8278        _elevation = stream.ReadVarInt32Nullable();
 279
 280        // read vertex locations.
 8281        var coordinateSize = CoordinateSizeInBytes * 2;
 8282        if (_elevation != null)
 2283        {
 2284            coordinateSize += ElevationSizeInBytes;
 2285        }
 286
 8287        var coordinateBytes = _nextVertexId * coordinateSize;
 8288        Array.Resize(ref _coordinates, (int)coordinateBytes);
 184289        for (var i = 0; i < coordinateBytes; i++)
 84290        {
 84291            _coordinates[i] = (byte)stream.ReadByte();
 84292        }
 293
 8294        _nextShapePointer = stream.ReadVarUInt32();
 8295        Array.Resize(ref _shapes, (int)_nextShapePointer);
 74296        for (var i = 0; i < _nextShapePointer; i++)
 29297        {
 29298            _shapes[i] = (byte)stream.ReadByte();
 29299        }
 8300    }
 301
 302    private void ReadGeoFrom(byte[] data, ref int offset)
 0303    {
 0304        _elevation = BitCoderBuffer.GetVarInt32Nullable(data, ref offset);
 305
 0306        var coordinateSize = CoordinateSizeInBytes * 2;
 0307        if (_elevation != null)
 0308        {
 0309            coordinateSize += ElevationSizeInBytes;
 0310        }
 311
 0312        var coordinateBytes = (int)(_nextVertexId * coordinateSize);
 0313        Array.Resize(ref _coordinates, coordinateBytes);
 0314        Buffer.BlockCopy(data, offset, _coordinates, 0, coordinateBytes);
 0315        offset += coordinateBytes;
 316
 0317        _nextShapePointer = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0318        Array.Resize(ref _shapes, (int)_nextShapePointer);
 0319        Buffer.BlockCopy(data, offset, _shapes, 0, (int)_nextShapePointer);
 0320        offset += (int)_nextShapePointer;
 0321    }
 322
 323    private void WriteGeoTo(byte[] data, ref int offset)
 0324    {
 0325        BitCoderBuffer.SetVarInt32Nullable(data, ref offset, _elevation);
 326
 0327        var coordinateSize = CoordinateSizeInBytes * 2;
 0328        if (_elevation != null)
 0329        {
 0330            coordinateSize += ElevationSizeInBytes;
 0331        }
 332
 0333        var coordinateBytes = (int)(_nextVertexId * coordinateSize);
 0334        Buffer.BlockCopy(_coordinates, 0, data, offset, coordinateBytes);
 0335        offset += coordinateBytes;
 336
 0337        BitCoderBuffer.SetVarUInt32(data, ref offset, _nextShapePointer);
 0338        Buffer.BlockCopy(_shapes, 0, data, offset, (int)_nextShapePointer);
 0339        offset += (int)_nextShapePointer;
 0340    }
 341}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.Serialization.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using Itinero.IO;
 4
 5namespace Itinero.Network.Tiles;
 6
 7internal partial class NetworkTile
 8{
 9    public void WriteTo(Stream stream)
 810    {
 11        const int version = 1;
 812        stream.WriteVarInt32(version);
 13
 14        // write tile id/zoom.
 815        stream.WriteVarInt32(_zoom);
 816        stream.WriteVarUInt32(_tileId);
 817        stream.WriteGuid(_edgeTypeMapId);
 18
 19        // write vertices and edges.
 820        this.WriteEdgesAndVerticesTo(stream);
 21
 22        // write attributes.
 823        this.WriteAttributesTo(stream);
 24
 25        // write shapes.
 826        this.WriteGeoTo(stream);
 27
 28        // write turn costs.
 829        this.WriteTurnCostsTo(stream);
 830    }
 31
 32    public static NetworkTile ReadFrom(Stream stream)
 833    {
 834        var version = stream.ReadVarInt32();
 835        if (version != 1)
 036        {
 037            throw new InvalidDataException("Cannot deserialize tiles: Invalid version #.");
 38        }
 39
 40        // read tile id.
 841        var zoom = stream.ReadVarInt32();
 842        var tileId = stream.ReadVarUInt32();
 843        var edgeTypeMapId = stream.ReadGuid();
 44
 45        // create the tile.
 846        var graphTile = new NetworkTile(zoom, tileId, edgeTypeMapId);
 47
 48        // read vertices and edges.
 849        graphTile.ReadEdgesAndVerticesFrom(stream);
 50
 51        // read attributes.
 852        graphTile.ReadAttributesFrom(stream);
 53
 54        // read shapes.
 855        graphTile.ReadGeoFrom(stream);
 56
 57        // read turn costs.
 858        graphTile.ReadTurnCostsFrom(stream);
 59
 860        return graphTile;
 861    }
 62
 63    internal static NetworkTile ReadFromBuffer(byte[] data, ref int offset)
 064    {
 065        var version = BitCoderBuffer.GetVarInt32(data, ref offset);
 066        if (version != 1)
 067        {
 068            throw new InvalidDataException("Cannot deserialize tiles: Invalid version #.");
 69        }
 70
 071        var zoom = BitCoderBuffer.GetVarInt32(data, ref offset);
 072        var tileId = BitCoderBuffer.GetVarUInt32(data, ref offset);
 073        var edgeTypeMapId = BitCoderBuffer.GetGuid(data, ref offset);
 74
 075        var graphTile = new NetworkTile(zoom, tileId, edgeTypeMapId);
 76
 077        graphTile.ReadEdgesAndVerticesFrom(data, ref offset);
 078        graphTile.ReadAttributesFrom(data, ref offset);
 079        graphTile.ReadGeoFrom(data, ref offset);
 080        graphTile.ReadTurnCostsFrom(data, ref offset);
 81
 082        return graphTile;
 083    }
 84
 85    public static NetworkTile ReadFrom(byte[] data, int offset)
 086    {
 087        return ReadFromBuffer(data, ref offset);
 088    }
 89
 90    public byte[] ToBytes()
 091    {
 092        var maxSize = this.GetSerializedSizeUpperBound();
 093        var buffer = new byte[maxSize];
 094        var offset = 0;
 095        this.WriteToBuffer(buffer, ref offset);
 096        if (offset == maxSize) return buffer;
 097        var result = new byte[offset];
 098        Buffer.BlockCopy(buffer, 0, result, 0, offset);
 099        return result;
 0100    }
 101
 102    internal void WriteToBuffer(byte[] data, ref int offset)
 0103    {
 104        const int version = 1;
 0105        BitCoderBuffer.SetVarInt32(data, ref offset, version);
 0106        BitCoderBuffer.SetVarInt32(data, ref offset, _zoom);
 0107        BitCoderBuffer.SetVarUInt32(data, ref offset, _tileId);
 0108        BitCoderBuffer.SetGuid(data, ref offset, _edgeTypeMapId);
 109
 0110        this.WriteEdgesAndVerticesTo(data, ref offset);
 0111        this.WriteAttributesTo(data, ref offset);
 0112        this.WriteGeoTo(data, ref offset);
 0113        this.WriteTurnCostsTo(data, ref offset);
 0114    }
 115
 116    internal int GetSerializedSizeUpperBound()
 0117    {
 0118        var size = 5 + 5 + 5 + 16;
 119
 0120        size += 5 + (int)_nextVertexId * 5;
 0121        size += 5 + (int)_nextEdgeId;
 0122        size += 5 + (int)_nextCrossTileId * 5;
 123
 0124        size += 5 + (int)_nextAttributePointer;
 0125        size += 5;
 0126        for (var i = 0; i < _nextStringId; i++)
 0127        {
 0128            size += 8 + System.Text.Encoding.Unicode.GetByteCount(_strings[i]);
 0129        }
 130
 0131        size += 5;
 0132        var coordinateSize = CoordinateSizeInBytes * 2;
 0133        if (_elevation != null) coordinateSize += ElevationSizeInBytes;
 0134        size += (int)(_nextVertexId * coordinateSize);
 0135        size += 5 + (int)_nextShapePointer;
 136
 0137        size += 5 + _turnCostPointers.Length * 5;
 0138        size += 5 + (int)_turnCostPointer;
 139
 0140        return size;
 0141    }
 142}

/home/runner/work/routing2/routing2/src/Itinero/Network/Tiles/NetworkTile.TurnCosts.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using Itinero.IO;
 6using Itinero.Network.Storage;
 7using Itinero.Network.TurnCosts;
 8
 9namespace Itinero.Network.Tiles;
 10
 11internal partial class NetworkTile
 12{
 62413    private uint _turnCostPointer = 0;
 62414    private uint[] _turnCostPointers = new uint[0];
 62415    private byte[] _turnCosts = new byte[0];
 16
 17    internal void AddTurnCosts(VertexId vertex, uint turnCostType,
 18        EdgeId[] edges, uint[,] costs, IEnumerable<(string key, string value)> attributes,
 19        IEnumerable<EdgeId>? prefix = null)
 479920    {
 479921        prefix ??= ArraySegment<EdgeId>.Empty;
 22
 479923        if (edges.Length > OrderCoder.MaxOrderHeadTail)
 024        {
 025            throw new ArgumentException(
 026                $"Cannot add turn costs for vertices with more than {OrderCoder.MaxOrderHeadTail} edges.");
 27        }
 28
 29        // enumerate the edges associated with the vertex.
 479930        var enumerator = new NetworkTileEnumerator();
 479931        enumerator.MoveTo(this);
 479932        if (!enumerator.MoveTo(vertex))
 033        {
 034            throw new ArgumentException($"Cannot add turn costs to a vertex that doesn't exist.",
 035                nameof(vertex));
 36        }
 37
 38        // determine max existing order, perhaps there are other turn costs already.
 479939        var orders = new byte?[edges.Length];
 479940        var max = -1;
 1503241        while (enumerator.MoveNext())
 1023342        {
 1497343            if (!enumerator.TailOrder.HasValue) continue;
 624044            if (enumerator.TailOrder.Value <= max) continue;
 45
 474646            max = enumerator.TailOrder.Value;
 474647        }
 48
 49        // assign missing orders if any.
 479950        enumerator.Reset();
 479951        var next = max + 1;
 1503252        while (enumerator.MoveNext())
 1023353        {
 54            // only assign orders of edges that are used.
 1023355            var i = Array.IndexOf(edges, enumerator.EdgeId);
 1086856            if (i == -1) continue;
 57
 58            // edge is used, assign or reuse existing.
 959859            var order = enumerator.TailOrder;
 959860            if (order == null)
 461761            {
 62                // get next order.
 461763                order = (byte)next;
 461764                next++;
 65
 66                // set order, it's different.
 461767                if (enumerator.Forward)
 229668                {
 69                    // if the edge is forward the tail in the enumerator is also the tail in the edge.
 229670                    this.SetTailHeadOrder(enumerator.EdgePointer, order, enumerator.HeadOrder);
 229671                }
 72                else
 232173                {
 74                    // if the edge is backward the tail in the enumerator is head in the edge.
 232175                    this.SetTailHeadOrder(enumerator.EdgePointer, enumerator.TailOrder, order);
 232176                }
 461777            }
 78
 959879            orders[i] = order;
 959880        }
 81
 82        // reversed order array.
 479983        var count = next;
 479984        var reversedOrders = new byte?[count];
 2879485        for (var i = 0; i < orders.Length; i++)
 959886        {
 959887            var order = orders[i];
 959888            if (order == null)
 089            {
 090                continue;
 91            }
 92
 959893            reversedOrders[order.Value] = (byte)i;
 959894        }
 95
 96        // make sure there is space in the pointers table.
 97        // and initialize new slots with null.
 494198        while (_turnCostPointers.Length <= vertex.LocalId)
 14299        {
 142100            Array.Resize(ref _turnCostPointers, (int)_nextVertexId);
 142101        }
 102
 103        // make sure there is space in the turn cost array.
 104        // and initialize new slots with null.
 4799105        var maxLength = _turnCostPointer + 5 + 1 + (count * count * 5) + 5;
 8613106        while (_turnCosts.Length <= maxLength)
 3814107        {
 3814108            Array.Resize(ref _turnCosts, _turnCosts.Length + DefaultSizeIncrease);
 3814109        }
 110
 111        // update pointer to reflect new data.
 4799112        var previousPointer = _turnCostPointers[(int)vertex.LocalId].DecodeNullableData();
 4799113        _turnCostPointers[(int)vertex.LocalId] = _turnCostPointer.EncodeToNullableData();
 114
 115        // write turn cost types.
 4799116        _turnCostPointer += _turnCosts.SetDynamicUInt32(_turnCostPointer, turnCostType);
 117
 118        // write attributes.
 4799119        var a = this.SetAttributes(attributes, null);
 4799120        _turnCostPointer += _turnCosts.SetDynamicUInt32(_turnCostPointer, a);
 121
 122        // write prefix sequence.
 123        // Use Int64 so the same-tile/foreign-tile signal (sign of the encoded value) round-trips
 124        // correctly for any uint LocalId, including CrossEdgeIds where LocalId >= 0x7FFFFFFF.
 125        // (Int32 would silently overflow-wrap to a positive value when LocalId has the high bit set,
 126        // making the reader misread foreign edges as same-tile and skip the tile id varint.)
 4799127        var prefixEdges = new List<EdgeId>(prefix);
 4799128        _turnCostPointer += _turnCosts.SetDynamicUInt32(_turnCostPointer, (uint)prefixEdges.Count);
 14397129        foreach (var prefixEdge in prefixEdges)
 0130        {
 0131            if (prefixEdge.TileId == _tileId)
 0132            {
 0133                _turnCostPointer += _turnCosts.SetDynamicInt64(_turnCostPointer, (long)prefixEdge.LocalId);
 0134            }
 135            else
 0136            {
 0137                _turnCostPointer += _turnCosts.SetDynamicInt64(_turnCostPointer, -(long)prefixEdge.LocalId - 1);
 0138                _turnCostPointer += _turnCosts.SetDynamicUInt32(_turnCostPointer, prefixEdge.TileId);
 0139            }
 0140        }
 141
 142        // write turn costs.
 4799143        _turnCosts[(int)_turnCostPointer] = (byte)count;
 4799144        _turnCostPointer++;
 29818145        for (var x = 0; x < count; x++)
 10110146        {
 10110147            var xOrder = reversedOrders[x];
 64332148            for (var y = 0; y < count; y++)
 22056149            {
 22056150                var yOrder = reversedOrders[y];
 151
 152                // get cost from original matrix.
 22056153                uint cost = 0;
 22056154                if (xOrder != null && yOrder != null)
 19196155                {
 19196156                    cost = costs[xOrder.Value, yOrder.Value];
 19196157                }
 158
 159                // write cost.
 22056160                _turnCostPointer += (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, cost);
 22056161            }
 10110162        }
 163
 164        // write previous turn cost pointer at the end.
 4799165        _turnCostPointer +=
 4799166            (uint)_turnCosts.SetDynamicUInt32(_turnCostPointer, previousPointer.EncodeAsNullableData());
 4799167    }
 168
 169    internal IEnumerable<(uint turnCostType, IEnumerable<(string key, string value)> attributes, uint cost,
 170            IEnumerable<EdgeId> prefixEdges)>
 171        GetTurnCosts(VertexId vertex, byte fromOrder, byte toOrder)
 19205172    {
 19205173        if (_turnCostPointers.Length <= vertex.LocalId)
 0174        {
 0175            yield break;
 176        }
 177
 19205178        var pointerNullable = _turnCostPointers[(int)vertex.LocalId].DecodeNullableData();
 19205179        if (pointerNullable == null)
 0180        {
 0181            yield break;
 182        }
 183
 19205184        var pointer = pointerNullable.Value;
 40800185        while (true)
 40800186        {
 187            // return turn cost type.
 40800188            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var turnCostType);
 189
 190            // read attributes.
 40800191            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var a);
 40800192            var attributes = this.GetAttributes(a);
 193
 194            // read prefix edges.
 195            // Pair with the Int64 writer above; Int32 would mis-decode the sign for CrossEdgeIds.
 40800196            IEnumerable<EdgeId> prefixEdges = ArraySegment<EdgeId>.Empty;
 40800197            pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var prefixEdgeCount);
 40800198            if (prefixEdgeCount > 0)
 0199            {
 0200                var prefixEdgesList = new List<EdgeId>();
 0201                while (prefixEdgeCount > 0)
 0202                {
 0203                    pointer += (uint)_turnCosts.GetDynamicInt64(pointer, out var signedLocalId);
 0204                    if (signedLocalId >= 0)
 0205                    {
 0206                        prefixEdgesList.Add(new EdgeId(this.TileId, (uint)signedLocalId));
 0207                    }
 208                    else
 0209                    {
 0210                        pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var tileId);
 0211                        prefixEdgesList.Add(new EdgeId(tileId, (uint)(-signedLocalId - 1)));
 0212                    }
 213
 0214                    prefixEdgeCount--;
 0215                }
 216
 0217                prefixEdges = prefixEdgesList;
 0218            }
 219
 220            // read turn cost table.
 40800221            var max = _turnCosts[(int)pointer];
 40800222            pointer++;
 223
 250782224            for (var x = 0; x < max; x++)
 84654225            {
 526072226                for (var y = 0; y < max; y++)
 178445227                {
 178445228                    pointer += (uint)_turnCosts.GetDynamicUInt32(pointer, out var cost);
 178445229                    if (fromOrder != x || toOrder != y)
 138030230                    {
 138030231                        continue;
 232                    }
 233
 40415234                    if (cost != 0)
 19204235                    {
 19204236                        yield return (turnCostType, attributes, cost, prefixEdges);
 19141237                    }
 40352238                }
 84591239            }
 240
 241            // get pointer to next turn cost table.
 40737242            _turnCosts.GetDynamicUInt32(pointer, out var p);
 40737243            pointerNullable = p.DecodeNullableData();
 40737244            if (pointerNullable == null)
 19142245            {
 19142246                break;
 247            }
 248
 21595249            pointer = pointerNullable.Value;
 21595250        }
 19142251    }
 252
 253    internal void SetTailHeadOrder(uint pointer, byte? tailOrder, byte? headOrder)
 4629254    {
 255        // skip over vertices and next-pointers.
 4629256        uint size = this.DecodeVertex(pointer, out _, out var t1);
 4629257        pointer += size;
 4629258        size = this.DecodeVertex(pointer, out _, out var t2);
 4629259        pointer += size;
 4629260        size = this.DecodePointer(pointer, out _);
 4629261        pointer += size;
 4629262        size = this.DecodePointer(pointer, out _);
 4629263        pointer += size;
 264
 265        // skip edge id if needed.
 4629266        if (t1 != t2)
 101267        {
 101268            size = _edges.GetDynamicUInt32(pointer, out _);
 101269            pointer += size;
 101270        }
 271
 272        // skip over length/type.
 4629273        size = this.DecodeEdgePointerId(pointer, out _);
 4629274        pointer += size;
 4629275        size = this.DecodeEdgePointerId(pointer, out _);
 4629276        pointer += size;
 277
 278        // get existing head/tail order.
 4629279        byte? existingTailOrder = null;
 4629280        byte? existingHeadOrder = null;
 4629281        this.GetTailHeadOrder(pointer, ref existingTailOrder, ref existingHeadOrder);
 282
 283        // set tail order if there is a value.
 4629284        if (tailOrder.HasValue)
 2298285        {
 2298286            if (existingTailOrder.HasValue && existingTailOrder.Value != tailOrder.Value)
 0287                throw new InvalidOperationException("An edge tail or head order can only be set once.");
 2298288            existingTailOrder = tailOrder;
 2298289        }
 290
 291        // set head order if there is a value.
 4629292        if (headOrder.HasValue)
 2437293        {
 2437294            if (existingHeadOrder.HasValue && existingHeadOrder.Value != headOrder.Value)
 0295                throw new InvalidOperationException("An edge tail or head order can only be set once.");
 2437296            existingHeadOrder = headOrder;
 2437297        }
 298
 4629299        _edges.SetTailHeadOrder(pointer, existingTailOrder, existingHeadOrder);
 4629300    }
 301
 302    private void WriteTurnCostsTo(Stream stream)
 8303    {
 8304        stream.WriteVarUInt32((uint)_turnCostPointers.Length);
 16305        for (var i = 0; i < _turnCostPointers.Length; i++)
 0306        {
 0307            stream.WriteVarUInt32(_turnCostPointers[i]);
 0308        }
 309
 8310        stream.WriteVarUInt32(_turnCostPointer);
 16311        for (var i = 0; i < _turnCostPointer; i++)
 0312        {
 0313            stream.WriteByte(_turnCosts[i]);
 0314        }
 8315    }
 316
 317    private void ReadTurnCostsFrom(Stream stream)
 8318    {
 8319        var turnCostPointersSize = stream.ReadVarUInt32();
 8320        Array.Resize(ref _turnCostPointers, (int)turnCostPointersSize);
 16321        for (var i = 0; i < turnCostPointersSize; i++)
 0322        {
 0323            _turnCostPointers[i] = stream.ReadVarUInt32();
 0324        }
 325
 8326        _turnCostPointer = stream.ReadVarUInt32();
 8327        Array.Resize(ref _turnCosts, (int)_turnCostPointer);
 16328        for (var i = 0; i < _turnCostPointer; i++)
 0329        {
 0330            _turnCosts[i] = (byte)stream.ReadByte();
 0331        }
 8332    }
 333
 334    private void ReadTurnCostsFrom(byte[] data, ref int offset)
 0335    {
 0336        var turnCostPointersSize = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0337        Array.Resize(ref _turnCostPointers, (int)turnCostPointersSize);
 0338        for (var i = 0; i < turnCostPointersSize; i++)
 0339        {
 0340            _turnCostPointers[i] = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0341        }
 342
 0343        _turnCostPointer = BitCoderBuffer.GetVarUInt32(data, ref offset);
 0344        Array.Resize(ref _turnCosts, (int)_turnCostPointer);
 0345        Buffer.BlockCopy(data, offset, _turnCosts, 0, (int)_turnCostPointer);
 0346        offset += (int)_turnCostPointer;
 0347    }
 348
 349    private void WriteTurnCostsTo(byte[] data, ref int offset)
 0350    {
 0351        BitCoderBuffer.SetVarUInt32(data, ref offset, (uint)_turnCostPointers.Length);
 0352        for (var i = 0; i < _turnCostPointers.Length; i++)
 0353        {
 0354            BitCoderBuffer.SetVarUInt32(data, ref offset, _turnCostPointers[i]);
 0355        }
 356
 0357        BitCoderBuffer.SetVarUInt32(data, ref offset, _turnCostPointer);
 0358        Buffer.BlockCopy(_turnCosts, 0, data, offset, (int)_turnCostPointer);
 0359        offset += (int)_turnCostPointer;
 0360    }
 361}