feat: CUID & fix: reorder bug
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using CarManagerV3.Util;
|
||||
|
||||
namespace CarManagerV3
|
||||
{
|
||||
@@ -115,12 +116,15 @@ namespace CarManagerV3
|
||||
int numericId = 0;
|
||||
if ((string.IsNullOrWhiteSpace(id) || int.TryParse(id, out numericId)) && id != "0")
|
||||
{
|
||||
id = Guid.NewGuid().ToString();
|
||||
id = CUID.NewCUID().ToString();
|
||||
if (numericId > 0)
|
||||
{
|
||||
order = numericId + order;
|
||||
}
|
||||
|
||||
}
|
||||
if(id.Length > 8)
|
||||
{
|
||||
id = CUID.NewCUID().ToString();
|
||||
}
|
||||
// Sets the properties using the setters to ensure validation is applied.
|
||||
this.id = id;
|
||||
|
||||
245
CarManagerV3/Forms/MainForm.Designer.cs
generated
245
CarManagerV3/Forms/MainForm.Designer.cs
generated
@@ -29,171 +29,170 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.flpCars = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.tbxSearch = new System.Windows.Forms.TextBox();
|
||||
this.btnNewCar = new System.Windows.Forms.Button();
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.recentFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.revealInFileExplorerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
flpCars = new System.Windows.Forms.FlowLayoutPanel();
|
||||
tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
tbxSearch = new System.Windows.Forms.TextBox();
|
||||
btnNewCar = new System.Windows.Forms.Button();
|
||||
menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
recentFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
revealInFileExplorerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
tableLayoutPanel1.SuspendLayout();
|
||||
tableLayoutPanel2.SuspendLayout();
|
||||
menuStrip1.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 1;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.flpCars, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.menuStrip1, 0, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(802, 458);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
this.tableLayoutPanel1.Paint += new System.Windows.Forms.PaintEventHandler(this.tableLayoutPanel1_Paint);
|
||||
tableLayoutPanel1.ColumnCount = 1;
|
||||
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
tableLayoutPanel1.Controls.Add(flpCars, 0, 2);
|
||||
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 1);
|
||||
tableLayoutPanel1.Controls.Add(menuStrip1, 0, 0);
|
||||
tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
tableLayoutPanel1.RowCount = 3;
|
||||
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F));
|
||||
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
tableLayoutPanel1.Size = new System.Drawing.Size(802, 572);
|
||||
tableLayoutPanel1.TabIndex = 0;
|
||||
tableLayoutPanel1.Paint += tableLayoutPanel1_Paint;
|
||||
//
|
||||
// flpCars
|
||||
//
|
||||
this.flpCars.AutoScroll = true;
|
||||
this.flpCars.AutoScrollMargin = new System.Drawing.Size(0, 200);
|
||||
this.flpCars.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flpCars.Location = new System.Drawing.Point(3, 67);
|
||||
this.flpCars.Name = "flpCars";
|
||||
this.flpCars.Size = new System.Drawing.Size(796, 412);
|
||||
this.flpCars.TabIndex = 1;
|
||||
flpCars.AutoScroll = true;
|
||||
flpCars.AutoScrollMargin = new System.Drawing.Size(0, 200);
|
||||
flpCars.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
flpCars.Location = new System.Drawing.Point(3, 82);
|
||||
flpCars.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
flpCars.Name = "flpCars";
|
||||
flpCars.Size = new System.Drawing.Size(796, 515);
|
||||
flpCars.TabIndex = 1;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 2;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.tbxSearch, 0, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnNewCar, 1, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 27);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 34F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(796, 34);
|
||||
this.tableLayoutPanel2.TabIndex = 2;
|
||||
tableLayoutPanel2.ColumnCount = 2;
|
||||
tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.Controls.Add(tbxSearch, 0, 0);
|
||||
tableLayoutPanel2.Controls.Add(btnNewCar, 1, 0);
|
||||
tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
tableLayoutPanel2.Location = new System.Drawing.Point(3, 32);
|
||||
tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
tableLayoutPanel2.RowCount = 1;
|
||||
tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 42F));
|
||||
tableLayoutPanel2.Size = new System.Drawing.Size(796, 42);
|
||||
tableLayoutPanel2.TabIndex = 2;
|
||||
//
|
||||
// tbxSearch
|
||||
//
|
||||
this.tbxSearch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbxSearch.Location = new System.Drawing.Point(3, 3);
|
||||
this.tbxSearch.Name = "tbxSearch";
|
||||
this.tbxSearch.Size = new System.Drawing.Size(392, 22);
|
||||
this.tbxSearch.TabIndex = 3;
|
||||
this.tbxSearch.TextChanged += new System.EventHandler(this.tbxSearch_TextChanged);
|
||||
tbxSearch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
tbxSearch.Location = new System.Drawing.Point(3, 4);
|
||||
tbxSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
tbxSearch.Name = "tbxSearch";
|
||||
tbxSearch.Size = new System.Drawing.Size(392, 27);
|
||||
tbxSearch.TabIndex = 3;
|
||||
tbxSearch.TextChanged += tbxSearch_TextChanged;
|
||||
//
|
||||
// btnNewCar
|
||||
//
|
||||
this.btnNewCar.Location = new System.Drawing.Point(401, 3);
|
||||
this.btnNewCar.Name = "btnNewCar";
|
||||
this.btnNewCar.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnNewCar.TabIndex = 4;
|
||||
this.btnNewCar.Text = "Add Car";
|
||||
this.btnNewCar.UseVisualStyleBackColor = true;
|
||||
this.btnNewCar.Click += new System.EventHandler(this.btnNewCar_Click);
|
||||
btnNewCar.Location = new System.Drawing.Point(401, 4);
|
||||
btnNewCar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
btnNewCar.Name = "btnNewCar";
|
||||
btnNewCar.Size = new System.Drawing.Size(75, 29);
|
||||
btnNewCar.TabIndex = 4;
|
||||
btnNewCar.Text = "Add Car";
|
||||
btnNewCar.UseVisualStyleBackColor = true;
|
||||
btnNewCar.Click += btnNewCar_Click;
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.fileToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(802, 24);
|
||||
this.menuStrip1.TabIndex = 3;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem });
|
||||
menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
menuStrip1.Name = "menuStrip1";
|
||||
menuStrip1.Size = new System.Drawing.Size(802, 28);
|
||||
menuStrip1.TabIndex = 3;
|
||||
menuStrip1.Text = "menuStrip1";
|
||||
//
|
||||
// fileToolStripMenuItem
|
||||
//
|
||||
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.openToolStripMenuItem,
|
||||
this.saveToolStripMenuItem,
|
||||
this.saveAsToolStripMenuItem,
|
||||
this.importToolStripMenuItem,
|
||||
this.recentFilesToolStripMenuItem,
|
||||
this.revealInFileExplorerToolStripMenuItem});
|
||||
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
|
||||
this.fileToolStripMenuItem.Text = "File";
|
||||
fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { openToolStripMenuItem, saveToolStripMenuItem, saveAsToolStripMenuItem, importToolStripMenuItem, recentFilesToolStripMenuItem, revealInFileExplorerToolStripMenuItem });
|
||||
fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||
fileToolStripMenuItem.Size = new System.Drawing.Size(46, 24);
|
||||
fileToolStripMenuItem.Text = "File";
|
||||
//
|
||||
// openToolStripMenuItem
|
||||
//
|
||||
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||
this.openToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.openToolStripMenuItem.Text = "Open";
|
||||
this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
|
||||
openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||
openToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
openToolStripMenuItem.Text = "Open";
|
||||
openToolStripMenuItem.Click += openToolStripMenuItem_Click;
|
||||
//
|
||||
// saveToolStripMenuItem
|
||||
//
|
||||
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
|
||||
this.saveToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.saveToolStripMenuItem.Text = "Save";
|
||||
this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
|
||||
saveToolStripMenuItem.Name = "saveToolStripMenuItem";
|
||||
saveToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
saveToolStripMenuItem.Text = "Save";
|
||||
saveToolStripMenuItem.Click += saveToolStripMenuItem_Click;
|
||||
//
|
||||
// saveAsToolStripMenuItem
|
||||
//
|
||||
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
|
||||
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.saveAsToolStripMenuItem.Text = "Save as";
|
||||
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click);
|
||||
saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
|
||||
saveAsToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
saveAsToolStripMenuItem.Text = "Save as";
|
||||
saveAsToolStripMenuItem.Click += saveAsToolStripMenuItem_Click;
|
||||
//
|
||||
// importToolStripMenuItem
|
||||
//
|
||||
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
|
||||
this.importToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.importToolStripMenuItem.Text = "Import";
|
||||
this.importToolStripMenuItem.Click += new System.EventHandler(this.importToolStripMenuItem_Click);
|
||||
importToolStripMenuItem.Name = "importToolStripMenuItem";
|
||||
importToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
importToolStripMenuItem.Text = "Import";
|
||||
importToolStripMenuItem.Click += importToolStripMenuItem_Click;
|
||||
//
|
||||
// recentFilesToolStripMenuItem
|
||||
//
|
||||
this.recentFilesToolStripMenuItem.Name = "recentFilesToolStripMenuItem";
|
||||
this.recentFilesToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.recentFilesToolStripMenuItem.Text = "Recent Files";
|
||||
this.recentFilesToolStripMenuItem.Click += new System.EventHandler(this.recentFilesToolStripMenuItem_Click);
|
||||
recentFilesToolStripMenuItem.Name = "recentFilesToolStripMenuItem";
|
||||
recentFilesToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
recentFilesToolStripMenuItem.Text = "Recent Files";
|
||||
recentFilesToolStripMenuItem.Click += recentFilesToolStripMenuItem_Click;
|
||||
//
|
||||
// revealInFileExplorerToolStripMenuItem
|
||||
//
|
||||
this.revealInFileExplorerToolStripMenuItem.Name = "revealInFileExplorerToolStripMenuItem";
|
||||
this.revealInFileExplorerToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
|
||||
this.revealInFileExplorerToolStripMenuItem.Text = "Reveal in File Explorer";
|
||||
this.revealInFileExplorerToolStripMenuItem.Click += new System.EventHandler(this.revealInFileExplorerToolStripMenuItem_Click);
|
||||
revealInFileExplorerToolStripMenuItem.Name = "revealInFileExplorerToolStripMenuItem";
|
||||
revealInFileExplorerToolStripMenuItem.Size = new System.Drawing.Size(238, 26);
|
||||
revealInFileExplorerToolStripMenuItem.Text = "Reveal in File Explorer";
|
||||
revealInFileExplorerToolStripMenuItem.Click += revealInFileExplorerToolStripMenuItem_Click;
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(802, 458);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.MinimumSize = new System.Drawing.Size(818, 497);
|
||||
this.Name = "MainForm";
|
||||
this.Text = "Carmanager 3";
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.tableLayoutPanel2.PerformLayout();
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
|
||||
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
ClientSize = new System.Drawing.Size(802, 572);
|
||||
Controls.Add(tableLayoutPanel1);
|
||||
Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon");
|
||||
MainMenuStrip = menuStrip1;
|
||||
Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
MinimumSize = new System.Drawing.Size(818, 609);
|
||||
Name = "MainForm";
|
||||
Text = "Carmanager 3";
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
tableLayoutPanel1.PerformLayout();
|
||||
tableLayoutPanel2.ResumeLayout(false);
|
||||
tableLayoutPanel2.PerformLayout();
|
||||
menuStrip1.ResumeLayout(false);
|
||||
menuStrip1.PerformLayout();
|
||||
ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace CarManagerV3
|
||||
int temp = car.Order;
|
||||
car.Order = other.Order;
|
||||
other.Order = temp;
|
||||
cars = StateManager.normalizeOrders(cars);
|
||||
SafeManager.SaveCars(filepath, cars);
|
||||
refreshCars(cars);
|
||||
}
|
||||
@@ -155,6 +156,7 @@ namespace CarManagerV3
|
||||
int temp = car.Order;
|
||||
car.Order = other.Order;
|
||||
other.Order = temp;
|
||||
cars = StateManager.normalizeOrders(cars);
|
||||
SafeManager.SaveCars(filepath, cars);
|
||||
refreshCars(cars);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
118
CarManagerV3/Util/CUID.cs
Normal file
118
CarManagerV3/Util/CUID.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV3.Util
|
||||
{
|
||||
internal class CUID
|
||||
{
|
||||
public const int DefaultLength = 6;
|
||||
|
||||
private static int _counter = RandomNumberGenerator.GetInt32(int.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random CUID (Collision-resistant Unique Identifier) of a specified length.
|
||||
/// The CUID is designed to be unique across different machines and time, making it suitable for use as an identifier for cars in the application.
|
||||
/// The length must be between 4 and 32 characters to ensure a good balance between uniqueness and readability.
|
||||
/// The generated CUID consists of a combination of alphanumeric characters to ensure uniqueness and readability.
|
||||
/// </summary>
|
||||
/// <param name="length">The desired length of the generated CUID</param>
|
||||
/// <param name="prefixWithC">Whether to prefix the CUID with 'c' for better readability and to avoid starting with a digit.</param>"
|
||||
/// <returns></returns>
|
||||
public static string NewCUID(int length = DefaultLength, bool prefixWithC = true)
|
||||
{
|
||||
// CUIDv2 specs allow between 4 and 32 chars.
|
||||
if(length < 4 || length > 32) throw new ArgumentOutOfRangeException("length");
|
||||
|
||||
// We will produce enough encoded chars to satisfy 'length' after prefixing and truncation.
|
||||
// Base64 encodes 3 bytes -> 4 chars. So bytesNeeded ≈ ceil(charsNeeded * 3/4).
|
||||
int charsNeeded = prefixWithC ? (length - 1) : length;
|
||||
int bytesNeeded = (int)Math.Ceiling(charsNeeded * 3.0 / 4.0);
|
||||
|
||||
Span<byte> material = stackalloc byte[32];
|
||||
FillMaterial(material);
|
||||
|
||||
byte[] outputBytes = ExpandWithSha256(material, bytesNeeded);
|
||||
|
||||
string encoded = Base64UrlEncode(outputBytes);
|
||||
|
||||
if (encoded.Length < charsNeeded)
|
||||
{
|
||||
// Extremely unlikely unless length is huge; ensure we have enough by expanding more.
|
||||
// (Kept as a guard; for typical lengths like 24-64, you're fine.)
|
||||
outputBytes = ExpandWithSha256(material, bytesNeeded + 32);
|
||||
encoded = Base64UrlEncode(outputBytes);
|
||||
}
|
||||
|
||||
string body = encoded.Substring(0, charsNeeded);
|
||||
return prefixWithC ? ("c" + body) : body;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void FillMaterial(Span<byte> dst32)
|
||||
{
|
||||
// Compose a payload with:
|
||||
// - 16 bytes random
|
||||
// - 8 bytes timestamp (UTC ticks)
|
||||
// - 4 bytes counter
|
||||
// - 4 bytes process/thread noise
|
||||
Span<byte> payload = stackalloc byte[16 + 8 + 4 + 4];
|
||||
|
||||
RandomNumberGenerator.Fill(payload.Slice(0, 16));
|
||||
|
||||
long ticks = DateTime.UtcNow.Ticks;
|
||||
BinaryPrimitives.WriteInt64LittleEndian(payload.Slice(16, 8), ticks);
|
||||
|
||||
int c = Interlocked.Increment(ref _counter);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(payload.Slice(24, 4), c);
|
||||
|
||||
// Some extra variability (not relied on for security)
|
||||
int noise = Environment.ProcessId ^ Thread.CurrentThread.ManagedThreadId ^ (int)Stopwatch.GetTimestamp();
|
||||
BinaryPrimitives.WriteInt32LittleEndian(payload.Slice(28, 4), noise);
|
||||
|
||||
// Hash to produce 32 bytes of uniformly distributed output
|
||||
SHA256.HashData(payload, dst32);
|
||||
}
|
||||
|
||||
private static byte[] ExpandWithSha256(ReadOnlySpan<byte> seed32, int bytesNeeded)
|
||||
{
|
||||
if (bytesNeeded <= 0) return Array.Empty<byte>();
|
||||
|
||||
// If <= 32 bytes, we can just take from seed32 by hashing once more for separation.
|
||||
// We'll use SHA256(seed || blockIndex) to generate blocks.
|
||||
int blocks = (int)Math.Ceiling(bytesNeeded / 32.0);
|
||||
byte[] result = new byte[blocks * 32];
|
||||
|
||||
Span<byte> input = stackalloc byte[32 + 4];
|
||||
seed32.CopyTo(input.Slice(0, 32));
|
||||
|
||||
for (int i = 0; i < blocks; i++)
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(input.Slice(32, 4), i);
|
||||
Span<byte> block = result.AsSpan(i * 32, 32);
|
||||
SHA256.HashData(input, block);
|
||||
}
|
||||
|
||||
if (result.Length == bytesNeeded) return result;
|
||||
|
||||
byte[] trimmed = new byte[bytesNeeded];
|
||||
Buffer.BlockCopy(result, 0, trimmed, 0, bytesNeeded);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
private static string Base64UrlEncode(byte[] bytes)
|
||||
{
|
||||
// Standard base64url without padding per RFC 4648 §5
|
||||
string b64 = Convert.ToBase64String(bytes);
|
||||
return b64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user