1// Key.h
2//
3// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
4//
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18//
19// You can alternatively use *this file* under the terms of the the MIT
20// license included in this package.
21
22#ifndef KEY_H
23#define KEY_H
24
25#include <new>
26
27#include <stdio.h>
28
29#include <SupportDefs.h>
30
31#include "Debug.h"
32#include "endianess.h"
33
34using std::nothrow;
35
36// Key
37/*!
38	\class Key
39	\brief Represents the on-disk structure for a key.
40
41	Unfortunately there exist two different key formats and one can not
42	always guess the right format from the data. That makes the implementation
43	of the class a bit messy. This and the endianess awareness code, that is
44	particularly ugly for the bitfields (hopefully at least correct).
45*/
46class Key : private key {
47public:
48	Key() {}
49	Key(const Key &k) : key(k) {}
50	~Key() {}
51
52	static Key* CastFrom(key* k)
53		{ return static_cast<Key*>(k); }
54	static const Key* CastFrom(const key* k)
55		{ return static_cast<const Key*>(k); }
56
57	void SetTo(uint32 dirID, uint32 objectID, uint64 offset, uint32 type,
58			   uint16 version)
59	{
60		k_dir_id = h2le(dirID);
61		k_objectid = h2le(objectID);
62		if (version == KEY_FORMAT_3_5) {
63			u.k_offset_v1.k_offset = h2le((uint32)offset);
64			u.k_offset_v1.k_uniqueness = h2le(type);
65		} else
66			_SetOffsetAndType(offset, type);
67	}
68
69	uint16 GuessVersion() const
70	{
71		// assume old format, unless detected otherwise
72		switch (_GetType()) {
73			case TYPE_DIRECT:
74			case TYPE_INDIRECT:
75			case TYPE_DIRENTRY:
76				return KEY_FORMAT_3_6;
77			default:
78				return KEY_FORMAT_3_5;
79		}
80	}
81	uint32 GetDirID() const { return le2h(k_dir_id); }
82	uint32 GetObjectID() const { return le2h(k_objectid); }
83	uint64 GetOffset(uint16 version) const
84	{
85		return (version == KEY_FORMAT_3_6 ? _GetOffset()
86										  : le2h(u.k_offset_v1.k_offset));
87	}
88//	uint64 GetOffset() const { return GetOffset(GuessVersion()); }
89	void SetOffset(uint64 offset, uint16 version)
90	{
91		if (version == KEY_FORMAT_3_6)
92			_SetOffsetAndType(offset, _GetType());
93		else
94			u.k_offset_v1.k_offset = h2le(offset);
95	}
96	uint16 GetType(uint16 version) const
97	{
98		// current version
99		if (version == KEY_FORMAT_3_6)
100			return _GetType();
101		// old version
102		switch (le2h(u.k_offset_v1.k_uniqueness)) {
103			case V1_SD_UNIQUENESS:
104				return TYPE_STAT_DATA;
105			case V1_INDIRECT_UNIQUENESS:
106				return TYPE_INDIRECT;
107			case V1_DIRECT_UNIQUENESS:
108				return TYPE_DIRECT;
109			case V1_DIRENTRY_UNIQUENESS:
110				return TYPE_DIRENTRY;
111			case V1_ANY_UNIQUENESS:
112			default:
113				return TYPE_ANY;
114		}
115	}
116//	uint16 GetType() const { return GetType(GuessVersion()); }
117
118	Key &operator=(const Key &k)
119	{
120		*static_cast<key*>(this) = k;
121		return *this;
122	}
123
124private:
125	// helpers for accessing k_offset_v2
126	uint64 _GetOffset() const
127	{
128		#if LITTLE_ENDIAN
129			return u.k_offset_v2.k_type;
130		#else
131			offset_v2 temp;
132			*(uint64*)&temp = h2le(*(uint64*)&u.k_offset_v2);
133			return temp.k_offset;
134		#endif
135	}
136	uint32 _GetType() const
137	{
138		#if LITTLE_ENDIAN
139			return u.k_offset_v2.k_type;
140		#else
141			offset_v2 temp;
142			*(uint64*)&temp = h2le(*(uint64*)&u.k_offset_v2);
143			return temp.k_type;
144		#endif
145	}
146	void _SetOffsetAndType(uint64 offset, uint32 type)
147	{
148		u.k_offset_v2.k_offset = offset;
149		u.k_offset_v2.k_type = type;
150		#if !LITTLE_ENDIAN
151			*(uint64*)&u.k_offset_v2 = h2le(*(uint64*)&u.k_offset_v2);
152		#endif
153	}
154};
155
156
157// VKey	-- a versioned key
158/*!
159	\class VKey
160	\brief Wraps a Key and adds format version information.
161
162	This class is much more useful than Key. It knows its format version and
163	adds comparison operators. Note, that the operators do NOT compare the
164	type fields of the key. If that is needed, the Compare() method has
165	a flag for it.
166*/
167class VKey {
168private:
169	void _Unset() { if (fVersion & ALLOCATED) delete fKey; fKey = NULL; }
170
171public:
172	VKey() : fKey(NULL), fVersion(KEY_FORMAT_3_5) {}
173	VKey(const Key *k, uint32 version)
174		: fKey(const_cast<Key*>(k)), fVersion(version) {}
175	VKey(const Key *k) : fKey(NULL), fVersion(KEY_FORMAT_3_5) { SetTo(k); }
176	VKey(uint32 dirID, uint32 objectID, uint64 offset, uint32 type,
177		uint16 version)
178		: fKey(NULL), fVersion(KEY_FORMAT_3_5)
179	{
180		SetTo(dirID, objectID, offset, type, version);
181	}
182	VKey(const VKey &k)
183		: fKey(new(nothrow) Key(*k.fKey)), fVersion(k.fVersion | ALLOCATED) {}
184	~VKey() { _Unset(); }
185
186	void SetTo(const Key *k, uint32 version)
187	{
188		_Unset();
189		fKey = const_cast<Key*>(k);
190		fVersion = version;
191	}
192	void SetTo(const Key *k)
193	{
194		_Unset();
195		fKey = const_cast<Key*>(k);
196		fVersion = fKey->GuessVersion();
197	}
198	void SetTo(uint32 dirID, uint32 objectID, uint64 offset, uint32 type,
199			   uint16 version)
200	{
201		_Unset();
202		fKey = new(nothrow) Key;
203		if (version == KEY_FORMAT_3_5)
204			fVersion = KEY_FORMAT_3_5 | ALLOCATED;
205		else
206			fVersion = KEY_FORMAT_3_6 | ALLOCATED;
207		fKey->SetTo(dirID, objectID, offset, type, fVersion);
208	}
209
210	uint16 GetVersion() const { return fVersion & VERSION_MASK; }
211	uint32 GetDirID() const { return fKey->GetDirID(); }
212	uint32 GetObjectID() const { return fKey->GetObjectID(); }
213	uint64 GetOffset() const { return fKey->GetOffset(GetVersion()); }
214	uint16 GetType() const { return fKey->GetType(GetVersion()); }
215
216	void SetOffset(uint64 offset)
217		{ return fKey->SetOffset(offset, GetVersion()); }
218
219	int Compare(const VKey &k, bool compareTypes = false) const
220	{
221		if (GetDirID() < k.GetDirID())
222			return -1;
223		if (GetDirID() > k.GetDirID())
224			return 1;
225		if (GetObjectID() < k.GetObjectID())
226			return -1;
227		if (GetObjectID() > k.GetObjectID())
228			return 1;
229		int64 dOffset = (int64)GetOffset() - (int64)k.GetOffset();
230		if (dOffset < 0)
231			return -1;
232		if (dOffset > 0)
233			return 1;
234		if (compareTypes) {
235			int32 dType = (int32)GetType() - (int32)k.GetType();
236			if (dType < 0)
237				return -1;
238			if (dType > 0)
239				return 1;
240		}
241		return 0;
242	}
243	// Note: The operators don't compare the types! Use Compare(, true), if
244	// you want to do that.
245	bool operator==(const VKey &k) const { return (Compare(k) == 0); }
246	bool operator!=(const VKey &k) const { return (Compare(k) != 0); }
247	bool operator<(const VKey &k) const { return (Compare(k) < 0); }
248	bool operator>(const VKey &k) const { return (Compare(k) > 0); }
249	bool operator<=(const VKey &k) const { return (Compare(k) <= 0); }
250	bool operator>=(const VKey &k) const { return (Compare(k) >= 0); }
251
252	VKey &operator=(const VKey &k)
253	{
254		if (!(fVersion & ALLOCATED))
255			fKey = new(nothrow) Key;
256		*fKey = *k.fKey;
257		fVersion |= ALLOCATED;
258		return *this;
259	}
260
261	void Dump() const
262	{
263		TPRINT(("key: {%" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu64 ", %hu}\n",
264			GetDirID(), GetObjectID(), GetOffset(), GetType()));
265	}
266
267private:
268	enum {
269		VERSION_MASK	= KEY_FORMAT_3_5 | KEY_FORMAT_3_6,
270		ALLOCATED		= 0x8000
271	};
272
273private:
274	Key		*fKey;
275	uint16	fVersion;
276};
277
278#endif	// KEY_H
279